“字符”是一个有点模棱两可的术语,它在不同的上下文中意味着不同的东西。我猜您想要的结果与您的示例相同,[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]]