2

我需要将 UTF-8 字符串中的字节位置转换为 Objective-C 中相应的字符位置。我确信必须有一个库来执行此操作,但我找不到一个 - 任何人都可以(尽管显然任何 C 或 C++ 库都可以在这里完成这项工作)。

我意识到我可以在所需字符处截断 UTF-8 字符串,将其转换为 NSString,然后读取 NSString 的长度以获得我的答案,但这似乎是一个可以解决的问题的有点棘手的解决方案只需在 C 中使用一个小的 FSM。

谢谢你的帮助。

4

2 回答 2

1

“字符”是一个有点模棱两可的术语,它在不同的上下文中意味着不同的东西。我猜你想要和你的例子一样的结果,[NSString length].

NSString文档并没有完全提前说明这一点,而是计算[NSString length]了字符串中UTF-16 代码单元的数量。所以 U+0000..U+FFFF 每个算一个,但 U+10000..U+10FFFF 每个算两个。并且不要拆分代理对!

您可以根据每个 UTF-8 字符的前导字节来计算 UTF-16 代码点的数量。尾随字节使用一组不相交的值,因此您根本不需要跟踪任何状态,除了您在字符串中的位置(好消息:有限状态机是多余的)。

static const unsigned char BYTE_WIDTHS[256] = {
    // 1-byte: 0xxxxxxx
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    // Trailing: 10xxxxxx
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    // 2-byte leading: 110xxxxx
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    // 3-byte leading: 1110xxxx
    // 4-byte leading: 11110xxx
    // invalid: 11111xxx
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0
};

size_t utf8_utf16width(const unsigned char *string, size_t len)
{
    size_t i, utf16len = 0;
    for (i = 0; i < len; i++)
        utf16len += BYTE_WIDTHS[string[i]];
    return utf16len;
}

该表是 1 表示 1 字节、2 字节和 3 字节 UTF-8 前导字符,2 表示 4 字节 UTF-8 前导字符,因为这些字符在转换为NSString.

我在 Haskell 中生成了表格:

elems $ listArray (0,256) (repeat 0) //
    [(n,1) | n <- ([0x00..0x7f] ++ [0xc0..0xdf] ++ [0xe0..0xef])] //
    [(n,2) | n <- [0xf0..0xf7]]
于 2013-02-23T09:06:47.483 回答
0

查看UTF-8 编码并注意代码点以以下 8 位模式开头:

76543210 <- bit
0xxxxxxx <- ASCII chars
110xxxxx \
1110xxxx  } <- more byte(s) (of form 10xxxxxx) follow
11110xxx /

这就是您在搜索代码点的开头时应该寻找的内容。

但这只是解决方案的一部分。您需要考虑Combining characters。您需要将变音符号与它们之前的主要字符组合在一起,您不能将它们分开并视为独立字符。

可能还有更多。

于 2013-02-23T08:48:13.023 回答