【问题标题】:C++ utf-8 literals in GCC and MSVCGCC 和 MSVC 中的 C++ utf-8 文字
【发布时间】:2020-08-13 18:46:05
【问题描述】:

这里有一些简单的代码:

#include <iostream>
#include <cstdint>

    int main()
    {
         const unsigned char utf8_string[] = u8"\xA0";
         std::cout << std::hex << "Size: " << sizeof(utf8_string) << std::endl;
          for (int i=0; i < sizeof(utf8_string); i++) {
            std::cout << std::hex << (uint16_t)utf8_string[i] << std::endl;
          }
    }

我在这里看到 MSVC 和 GCC 的不同行为。 MSVC 将"\xA0" 视为未编码的 unicode 序列,并将其编码为 utf-8。 所以在 MSVC 中的输出是:

C2A0

在 utf8 unicode 符号 U+00A0 中正确编码。

但在 GCC 的情况下不会发生任何事情。它将字符串视为简单字节。即使我在字符串文字之前删除 u8 也没有任何变化。

如果字符串设置为:u8"\u00A0";,则两个编译器都编码为 utf8 并输出 C2A0u8"\u00A0";

为什么编译器的行为会有所不同,哪些实际上是正确的?

用于测试的软件:

GCC 8.3.0

MSVC 19.00.23506

C++ 11

【问题讨论】:

  • 你在 MSVC 上得到了什么输出(PS:我没有)?在g++clang 我得到这个:Size: 2 a0 0
  • u8输出是标准的,必须是UTF-8。但是对u8输入 的解释可能不是标准的。 \uXXXX\UXXXXXXXX 的行为是标准的,它们必须被解释为代码点。但是\xXX 的解释更多是由实现定义的。 \xA0 可以按原样解释为单个 char 0xA0,也可以扩展为代码点 U+00A0,然后编码为 2 chars 0xC2 0xA0。您会看到这两种行为。
  • @RemyLebeau 你能为“\xA0 可能(..)扩展为代码点”提供一些参考吗,因为en.cppreference.com/w/cpp/language/escape\xnn 是一个字节而不是一个代码点。
  • @MrLister cppreference.com 通常是可靠的,但并非绝对可靠。鉴于 UTF-8 具有成为有效字节序列的规则,因此编译器可以在必要时应用一些翻译并不是不合理的。
  • character literal U8'\xA0' 格式不正确(因为代码点不能用单个 UTF-8 代码单元表示),所以我希望一个带有这种文字的字符串会也会导致程序格式错误。

标签: c++ gcc visual-c++ unicode utf-8


【解决方案1】:

他们都错了。

据我所知,C++17 标准说here

窄字符串字面量的大小是转义序列和其他字符的总数,加上每个通用字符名称的多字节编码至少一个,加上终止 '\0' 的一个。

虽然还有其他提示,但这似乎是转义序列不是多字节并且 MSVC 的行为是错误的最有力的迹象。

目前有一些票被标记为正在调查中:

不过,它也提到 here 关于 UTF-8 文字:

如果该值不能用单个 UTF-8 代码单元表示,则程序格式错误。

由于 0xA0 不是有效的 UTF-8 字符,程序不应编译。

请注意:

  • u8 开头的UTF-8 文字被定义为窄。
  • \xA0 是一个转义序列
  • \u00A0 被视为通用字符名称,而不是转义序列

【讨论】:

  • "ill-formed" 意味着程序不应该在没有发出诊断信息的情况下被编译。该标准允许编译这样的程序并将这样的行为称为扩展。
  • “它是一个扩展”和“它是错误的”之间只有一线之隔。从标准的角度来看,可以说前者的每一个实例都是后者的一个例子。
  • 关于可表示性的评论是参考字符文字,这个问题是关于没有这种限制的字符串文字。所以没有冲突,这个答案的后半部分是不相关的。
【解决方案2】:

为什么编译器的行为会有所不同,哪些实际上是正确的?

编译器的行为因他们决定实现 C++ 标准的方式而异:

  • GCC 使用严格的规则并按原样执行标准
  • MSVC 使用松散的规则并以更实用的“真实世界”方式实施标准

因此,在 GCC 中失败的事情通常会在 MSVC 中起作用,因为它更允许。 MSVC 会自动处理其中一些问题。

这是一个类似的例子: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=33167。 它遵循标准,但不是您所期望的。

至于哪个对,就看你对“对”的定义是什么了。

【讨论】:

  • 但是 GCC 并没有按原样实现它
【解决方案3】:

我无法告诉你哪种方式符合标准。

MSVC 的做法至少在逻辑上是一致的并且易于解释。三个转义序列 \x\u\U 行为相同,除了它们从输入中提取的十六进制数字的数量:2、4 或 8。每个都定义了一个 Unicode 代码点,然后必须将其编码为 UTF -8。嵌入没有编码的字节可能会导致创建无效的 UTF-8 序列。

【讨论】:

    猜你喜欢
    • 2011-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-17
    • 1970-01-01
    • 1970-01-01
    • 2017-03-04
    • 2021-12-17
    相关资源
    最近更新 更多