将 Unicode 字符串作为字符循环

h4x*_*x04 2 c++ string unicode string-length

使用以下字符串,大小输出不正确。这是为什么,我该如何解决?

string str = " ??????";
cout << str.size();
// outputs 19 rather than 7
Run Code Online (Sandbox Code Playgroud)

我正在尝试str逐个字符地循环,以便我可以将其读入vector<string>大小为 7 的 a,但由于上述代码输出 19,因此我无法执行此操作。

phu*_*clv 6

TL; 博士

size()length()成员basic_string返回以底层字符串为单位的大小,而不是可见字符数量。要获得预期的数字:

  • 将 UTF-16 与u前缀用于非常简单的字符串,这些字符串不包含非 BMP、组合字符连接字符
  • U对于不包含任何组合或连接字符的非常简单的字符串,使用带有前缀的UTF-32
  • 规范化任意 Unicode 字符串的字符串和计数

" ??????"是一个空格,后跟一系列 6 个U+2588 个字符。你的编译器好像是用UTF-8std::string。UTF-8 是一种可变长度编码,许多字母使用多个字节进行编码(因为很明显,你不能只用一个字节编码超过 256 个字符)。在 UTF-8 中,U+0800 和 U+FFFF 之间的代码点由 3 个字节编码。因此 UTF-8 中字符串的长度为1 + 6*3 = 19个字节。

您可以使用任何像这样的Unicode 转换器进行检查,并查看字符串是否20 E2 96 88 E2 96 88 E2 96 88 E2 96 88 E2 96 88 E2 96 88以 UTF-8编码,您还可以遍历字符串的每个字节以进行检查

如果您想要字符串可见字符总数,那么它会更加棘手并且丘里尔的解决方案不起作用。阅读Twitter 中的示例

如果您使用的不是最基本的字母、数字和标点符号,情况就会变得更加混乱。虽然许多人使用多字节汉字字符来举例说明这些问题,但 Twitter 发现重音元音最容易引起混淆,因为说英语的人只是希望它们能正常工作。以下面的例子为例:“咖啡馆”这个词。事实证明,有两个字节序列看起来完全相同,但使用的字节数不同:

café  0x63 0x61 0x66 0xC3 0xA9        Using the “é” character, called the “composed character”.
café  0x63 0x61 0x66 0x65 0xCC 0x81   Using the combining diacritical, which overlaps the “e”
Run Code Online (Sandbox Code Playgroud)

你需要一个像ICU这样的 Unicode 库来规范化字符串和计数。例如 Twitter 使用规范化表格 C

编辑:

由于您只对似乎不在 BMP 之外且不包含任何组合字符的框绘图字符感兴趣,因此 UTF-16 和 UTF-32 将起作用。像std::string,std::wstring也是 abasic_string并且没有强制编码。在大多数实现中,它通常是 UTF-16 (Windows) 或 UTF-16 (*nix),因此您可以使用它,但它不可靠并且取决于源代码编码。更好的方法是使用std::u16stringstd::basic_string<char16_t>)和std::u32stringstd::basic_string<char32_t>)。无论源文件的系统和编码如何,它们都可以工作

std::wstring wstr     = L" ??????";
std::u16string u16str = u" ??????";
std::u32string u32str = U" ??????";
std::cout << str.size();    // may work, returns the number of wchar_t characters
std::cout << u16str.size(); // always returns the number of UTF-16 code units
std::cout << u32str.size(); // always returns the number of UTF-32 code units
Run Code Online (Sandbox Code Playgroud)

如果您对如何解决所有 Unicode 字符的问题感兴趣,请继续阅读以下内容

上面提到的“咖啡馆”问题引发了如何计算推文字符串“咖啡馆”中字符的问题。对于人眼来说,长度显然是四个字符。根据数据的表示方式,这可能是五个或六个 UTF-8 字节。Twitter 不想因为我们使用 UTF-8 或有问题的 API 客户端使用更长的表示而惩罚用户。因此,无论发送哪种表示形式,Twitter 都会将“咖啡馆”视为四个字符。

[...]

Twitter 使用文本的规范化形式 C (NFC) 版本计算推文的长度。这种类型的规范化倾向于使用完全组合的字符(咖啡馆示例中的 0xC3 0xA9)而不是长格式版本(0x65 0xCC 0x81)。Twitter 还计算文本中代码点的数量,而不是 UTF-8 字节。咖啡馆示例中的 0xC3 0xA9 是一个代码点 (U+00E9),在 UTF-8 中编码为两个字节,而 0x65 0xCC 0x81 是两个代码点,编码为三个字节

Twitter - 计算字符

也可以看看