我的平台是Mac。我是一名 C++ 初学者,正在从事一个处理中文和英文的个人项目。 UTF-8 是本项目的首选编码。
我在 Stack Overflow 上阅读了一些帖子,其中许多建议在处理 UTF-8 时使用 std::string
并避免使用 wchar_t
因为现在没有 char8_t
-8。
However, none of them talk about how to properly deal with functions like str[i]
, std::string::size()
, std::string::find_first_of()
or std::regex
as these function usually returns面对 UTF-8 时的意外结果。
我应该继续使用 std::string
还是切换到 std::wstring
?如果我应该继续使用 std::string
,那么处理上述问题的最佳做法是什么?
原文由 Saddle Point 发布,翻译遵循 CC BY-SA 4.0 许可协议
Unicode 词汇表
Unicode 是一个庞大而复杂的主题。我不想在那里涉水太深,但是需要一个快速的词汇表:
这是 Unicode 的基础。 Code Point 和 Grapheme Cluster 之间的区别几乎可以忽略,因为对于大多数现代语言,每个“字符”都映射到一个 Code Point(对于常用的字母 + 变音符号组合有专用的重音形式)。不过,如果您冒险使用笑脸、旗帜等……那么您可能需要注意区别。
UTF 入门
然后,必须对一系列 Unicode 代码点进行编码;常见的编码有 UTF-8、UTF-16 和 UTF-32,后两种既有 Little-Endian 又有 Big-Endian 的形式,共有 5 种常见编码。
在 UTF-X 中,X 是 Code Unit 的比特大小,每个 Code Point 表示为一个或多个 Code Unit,具体取决于其大小:
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),因此请做好准备在边界处转换(读取和写入)。wchar_t
确保一个代码单元代表一个完整的代码点,但它仍然不代表一个完整的字素簇。如果您只是阅读或编写字符串,那么
std::string
或std::wstring
应该没有任何问题。当您开始切片和切块时,麻烦就开始了,那么您必须注意 (1) 代码点边界(在 UTF-8 或 UTF-16 中)和 (2) Grapheme Clusters 边界。前者可以自己轻松处理,后者需要使用 Unicode 感知库。
选择
std::string
或std::u32string
?如果性能是一个问题,那么
std::string
可能会因为其较小的内存占用而表现更好;尽管大量使用中文可能会改变交易。一如既往,简介。如果 Grapheme Clusters 没有问题,那么
std::u32string
具有简化事情的优点:1 Code Unit -> 1 Code Point 意味着你不会意外拆分 Code Points,以及std::basic_string
的所有功能---
开箱即用。If you interface with software taking
std::string
orchar*
/char const*
, then stick tostd::string
to avoid back-and-forth conversions.否则会很痛苦。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。