【问题标题】:Is the u8 string literal necessary in C++11C++11中是否需要u8字符串文字
【发布时间】:2012-11-06 20:25:51
【问题描述】:

来自Wikipedia

为了增强 C++ 编译器对 Unicode 的支持,已将 char 类型的定义修改为至少存储 UTF-8 的 8 位编码所需的大小。

我想知道这对于编写可移植应用程序究竟意味着什么。写这个有什么区别吗

const char[] str = "Test String";

还是这个?

const char[] str = u8"Test String";

是否有任何理由不对代码中的每个字符串文字使用后者?

当 TestString 中有非 ASCII 字符时会发生什么?

【问题讨论】:

标签: c++ utf-8 c++11 literals string-literals


【解决方案1】:

编译器选择平台自然的本机编码。在典型的 POSIX 系统上,它可能会选择 ASCII 并且可能取决于环境对 ASCII 范围之外的字符值的设置。在大型机上,它可能会选择 EBCDIC。比较从文件或命令行接收到的字符串可能最适合使用本机字符集。但是,在处理使用 UTF-8 显式编码的文件时,最好使用 u8"..." 字符串。

也就是说,随着最近与字符编码相关的变化,C 和 C++ 中字符串处理的基本假设被打破:每个内部字符对象(charwchar_t 等)用于表示一个字符。对于每个字符对象仅代表某个字符的一个字节的 UTF-8 字符串,这显然不再适用。因此,所有字符串操作、字符分类等功能不一定适用于这些字符串。我们没有任何好的库来处理此类字符串以纳入标准。

【讨论】:

  • char 早就知道可能是多字节的(即假设每个字符一个字符的程序员做错了)。另一方面 wchar_t 需要固定宽度。不幸的是,Unicode 从根本上打破了关于“固定宽度”意味着什么的假设。
  • 我不一定不同意字符串在很长一段时间内被用于保存多字节编码的事实,但标准并未承认这一事实并将内部字符视为一个单元。所有处理字符串的标准设施仍然表现得好像字符只是一个单元!例如,如果子字符串的开头和/或结尾可以位于 Unicode 字符的中间,那么使用 s.substr(b, n) 并没有多大意义。即使wchar_t 字符串也有固定宽度的字符,例如组合字符。
  • @DietmarKühl:“但标准没有承认这一事实”,我认为您的意思是在库函数中。 c++ 标准本身一直承认多字节(每个字符)字符串的存在。例如,它建议/要求(我不记得究竟是哪个)main 参数是 MBCSes,这是 Windows 约定失败的地方——或者,标准未能正确标准化现有实践的地方...... ;-)
  • 不,标准承认多字节编码,包括在库中。例如,代码转换方面可以处理非法序列、存储宽字符的多字节表示的空间不足等。多个 chars-per-wchar_t 在许多地方得到确认和处理。 s.substr(b,n) 的问题不是库的问题,而是程序员认为它在字符级别而不是指定的代码单元级别运行的问题。
  • 我对 Unicode 的评论从根本上打破了“固定宽度”的含义,是关于字符组合等问题。鉴于 Unicode wchar_t 几乎是worthless
【解决方案2】:

你引用维基百科:

为了增强 C++ 编译器对 Unicode 的支持,已将 char 类型的定义修改为至少存储 UTF-8 的 8 位编码所需的大小。

嗯,“为了目的”是不正确的。 char一直保证至少8位,即CHAR_BIT一直要求≥8,这是由于C标准中char要求的范围。这是(引用 C++11 §17.5.1.5/1)“合并”到 C++ 标准中。

如果我应该猜测这种措辞变化的目的,那将只是为那些不知道对 C 标准的依赖性的读者澄清一些事情。

关于u8字面前缀的效果,它

  • 影响可执行文件中字符串的编码,但是

  • 不幸的是它影响类型。

因此,在"tørrfisk"u8"tørrfisk" 两种情况下,您都会得到char const[<i>n</i>]。但在前一种文字中,编码是为编译器选择的任何内容,例如使用拉丁文 1(或 Windows ANSI Western),字符为 8 个字节加上一个空字节,数组大小为 9。而在后一种文字中,编码保证为 UTF-8,其中“ø”将被编码为2 或 3 个字节(我记不太清了),用于稍大的数组大小。

【讨论】:

    【解决方案3】:

    如果编译器的执行字符集设置为UTF-8,不管u8是否使用都没有区别,因为编译器在两者中都将字符转换为UTF-8案例。

    但是,如果编译器执行字符集是系统的非 UTF8 代码页(例如 Visual C++ 的默认值),则在省略 u8 时可能无法正确处理非 ASCII 字符。例如,转换为宽字符串会崩溃,例如在 VS15 中:

    std::string narrowJapanese("スタークラフト");
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> convertWindows;
    std::wstring wide = convertWindows.from_bytes(narrowJapanese); // Unhandled C++ exception in xlocbuf.
    

    【讨论】:

    • 所以,让我们将“执行字符集”标准化为 utf-8,然后工作就完成了。 :)
    【解决方案4】:

    "Test String" 的编码是实现定义的系统编码(窄的,可能是多字节的编码)。

    u8"Test String" 的编码始终为 UTF-8。

    这些例子并不是很能说明问题。如果您在字符串中包含一些 Unicode 文字(例如 \U0010FFFF),那么您总是会得到这些文字(编码为 UTF-8),但它们是否可以在系统编码的字符串中表示,如果是,它们的值是什么将是,是实现定义的。

    如果有帮助,想象一下您正在 EBCDIC 机器上创作源代码。那么文字“测试字符串”总是在源文件本身中进行 EBCDIC 编码,但 u8-initialized 数组包含 UTF-8 编码值,而第一个数组包含 EBCDIC 编码值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-03
      • 2010-10-21
      • 2021-05-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多