如何在Objective-C / C / C ++中转换字符和字节位置

时间:2013-02-23 08:24:09

标签: c++ objective-c c utf-8 nsstring

我需要将UTF-8字符串中的字节位置转换为Objective-C中的相应字符位置。我确定必须有一个库才能做到这一点,但我找不到一个 - 有没有人(虽然显然任何C或C ++库都可以在这里完成工作)。

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

感谢您的帮助。

2 个答案:

答案 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。你需要将变音标记与它们之前的主要字符组合在一起,你不能将它们分开并视为独立的字符。

可能还有更多。