在 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
or它可能是以下的组合U+0061 LATIN SMALL LETTER A
and U+0308 COMBINING DIAERESIS
。幸运的是,只需选择第一个代码点就可以实现将第一个字母大写的目标。
如果你确实需要第一个字素簇,您必须超越第一个代码点来查看下一个代码点是否与其结合。对于许多语言来说,知道哪些代码点是“非空格”或“组合”或变体选择器就足够了。对于一些复杂的脚本(例如韩文?),您可能需要转向此Unicode 联盟技术报告 http://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries.