在UTF-8中,第一个字节的高位告诉您有多少个后续字节是同一
代码点的一部分。
0b0xxxxxxx: this byte is the entire code point
0b10xxxxxx: this byte is a continuation byte - this shouldn't occur at the start of a string
0b110xxxxx: this byte plus the next (which must be a continuation byte) form the code point
0b1110xxxx: this byte plus the next two form the code point
0b11110xxx: this byte plus the next three form the code point
这种模式可能会持续下去,但我认为有效的UTF-8编码从未使用超过四个字节来表示单个代码点。
如果您编写一个函数来计算设置为1的前导位数的数量,则可以使用它来确定在输入为有效UTF-8的情况下拆分字节序列以隔离第一个逻辑代码点的位置。 如果您想要针对无效的UTF-8进行加固,您需要编写更多的代码。
另一种方法是利用连续字节始终匹配模式0b10xxxxxx
的事实,因此您取第一个字节,然后只要下一个字节匹配该模式,就继续取字节。
std::size_t GetFirst(const std::string &text) {
if (text.empty()) return 0;
std::size_t length = 1;
while ((text[length] & 0b11000000) == 0b10000000) {
++length;
}
return length;
}
对于许多语言而言,一个码点通常映射到一个字符。但是人们所认为的单个字符可能更接近于Unicode所称的字形簇,即一种或多种代码点组合在一起以产生字形。
在您的示例中,ä可以用不同的方式表示:它可以是单个码点U+00E4 LATIN SMALL LETTER A WITH DIAERESIS,也可以是U+0061 LATIN SMALL LETTER A和U+0308 COMBINING DIAERESIS的组合。幸运的是,只选择第一个码点应该适用于您将首字母大写的目标。
如果您真的需要第一个字形簇,则必须查看第一个码点以外的内容,以查看下一个码点是否与其组合。对于许多语言,仅需知道哪些码点是“非间距”、“组合”或变体选择器即可。对于某些复杂的脚本(例如,韩文?),您可能需要参考此
Unicode Consortium technical report。