我需要将UTF-8字符串中的字节位置转换为Objective-C中的相应字符位置。我确定必须有一个库才能做到这一点,但我找不到一个 - 有没有人(虽然显然任何C或C ++库都可以在这里完成工作)。
我意识到我可以截断所需字符的UTF-8字符串,将其转换为NSString,然后读取NSString的长度以获得我的答案,但这似乎是一个有点hacky的解决方案,可以解决问题用C中的小FSM很简单地解决。
感谢您的帮助。
答案 0 :(得分: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字节,2字节和3字节的UTF-8前导字符,表为1;对于4字节的UTF-8前导字符,表为2,因为这些字符在转换为时将最终为两个字符NSString
。
我用Haskell生成了表:
elems $ listArray (0,256) (repeat 0) //
[(n,1) | n <- ([0x00..0x7f] ++ [0xc0..0xdf] ++ [0xe0..0xef])] //
[(n,2) | n <- [0xf0..0xf7]]
答案 1 :(得分:0)
查看UTF-8 encoding并注意代码点以以下8位模式开头:
76543210 <- bit
0xxxxxxx <- ASCII chars
110xxxxx \
1110xxxx } <- more byte(s) (of form 10xxxxxx) follow
11110xxx /
这是搜索代码点开头时应该查找的内容。
但仅此一点只是解决方案的一部分。您需要考虑Combining characters。你需要将变音标记与它们之前的主要字符组合在一起,你不能将它们分开并视为独立的字符。
可能还有更多。