Unicode词汇表
Unicode是一个庞大而复杂的主题。我不希望在那儿走得太深,但是有必要提供一个简短的词汇表:
- 代码点:代码点是Unicode的基本构建块,代码点只是映射为意义的整数。整数部分可容纳32位(实际上是24位),其含义可以是字母,变音符号,空格,符号,笑脸,半个旗标,甚至可以是“下一部分从右到左读取”。
- 字素簇:字素簇是语义相关的代码点的组,例如unicode中的标志通过关联两个代码点来表示;孤立地表示,这两个中的每一个都没有意义,但是在一个词素簇中,它们关联在一起表示一个标志。在某些脚本中,字素簇还用于将字母与变音符号配对。
这是Unicode的基础。因为大多数现代语言中的每个“字符”都映射到单个代码点(对于常用的字母+音素符号,有专用的重音形式),所以代码点和字素簇之间的区别可以被大部分掩盖。不过,如果您冒险使用笑脸,旗帜等,那么您可能必须注意区别。
UTF入门
然后,必须对一系列Unicode Code Points进行编码。通用编码为UTF-8,UTF-16和UTF-32,后两种以Little-Endian和Big-Endian形式存在,总共有5种通用编码。
在UTF-X中,X是代码单位的大小,每个代码点根据其大小表示为一个或几个代码单位:
- UTF-8:1到4个代码单位,
- UTF-16:1或2个代码单位,
- UTF-32:1个代码单位。
std::string
和std::wstring
。
std::wstring
如果您担心可移植性,请不要使用(wchar_t
在Windows上仅为16位)。使用std::u32string
替代(又名std::basic_string<char32_t>
)。
- 内存中的表示形式(
std::string
或std::wstring
)与磁盘上的表示形式(UTF-8,UTF-16或UTF-32)无关,因此请做好准备在边界处进行转换(读取和写入)。
- 虽然32位
wchar_t
可确保一个代码单元代表一个完整的代码点,但它仍不代表一个完整的字素簇。
如果你只阅读和撰写串,你应该没有与少的问题std::string
或std::wstring
。
当您开始切片和切块时,麻烦就开始了,那么您必须注意(1)代码点边界(在UTF-8或UTF-16中)和(2)字素簇边界。前者可以很容易地自行处理,后者需要使用Unicode感知库。
采摘std::string
还是std::u32string
?
如果需要考虑性能,则可能std::string
由于其较小的内存占用而表现更好。尽管大量使用中文可能会改变交易。一如既往,简介。
如果Grapheme Clusters没问题,那么它std::u32string
具有简化操作的优势:1个代码单位-> 1个代码点意味着您不会意外地拆分代码点,并且所有std::basic_string
工作功能都可以立即使用。
如果您使用std::string
或char*
/来连接软件char const*
,则请坚决std::string
避免来回转换。否则会很痛苦。
中的UTF-8 std::string
。
UTF-8实际上在中可以很好地工作std::string
。
大多数操作都是开箱即用的,因为UTF-8编码是自同步的并且与ASCII向后兼容。
由于编码点的编码方式不同,因此寻找编码点不会偶然匹配另一个编码点的中间部分:
str.find('\n')
作品,
str.find("...")
作品由字节匹配字节1,
str.find_first_of("\r\n")
作品如果搜索ASCII字符。
同样,regex
大多数情况下都应该开箱即用。由于字符序列("haha"
)只是字节序列("哈"
),因此基本搜索模式应该可以立即使用。
但是,请警惕字符类(例如[:alphanum:]
),因为它取决于正则表达式的风格和实现,它可能匹配也可能不匹配Unicode字符。
类似地,当心将中继器应用于非ASCII“字符”时,"哈?"
可能仅将最后一个字节视为可选字节;在这种情况下,请使用括号清楚地描述重复的字节序列:"(哈)?"
。
1 查找的关键概念是归一化和归类;这会影响所有比较操作。std::string
将始终逐字节比较(并因此进行排序),而无需考虑特定于语言或用法的比较规则。如果需要处理完整的规范化/归类,则需要完整的Unicode库,例如ICU。