C++ u8 文字 - Windows 上的意外编码

Posted

技术标签:

【中文标题】C++ u8 文字 - Windows 上的意外编码【英文标题】:C++ u8 literal - unexpected encoding on Windows 【发布时间】:2019-11-26 07:49:26 【问题描述】:

我确定我在这里遗漏了一些东西,但我正在将常规字符串文字(在 utf8 编码文档中)的内容与 u8 字符串文字进行比较,并且在 Windows 上,u8 编码文字不包含预期的 utf8在 Linux 上编码数据。

详情:

cpp文件是utf8编码的 C++17 已启用 在 Windows 上使用 vs 2019 编译 在 Linux 上使用 gcc 9.2.1 编译

代码如下:

#include <iostream>
#include <string>

struct HexCharStruct 
    unsigned char c;
    HexCharStruct(unsigned char _c) : c(_c)  
;

inline std::ostream& operator<<(std::ostream& o, const HexCharStruct& hs) 
    return (o << std::hex << (int)hs.c);


inline HexCharStruct hex(unsigned char _c) 
    return HexCharStruct(_c);


int main( int argc, char** argv ) 

    std::string s1 = "????";
    std::string s2 = u8"????";

    std::cout << "s1: ";
    for (const char& c : s1)
        std::cout << hex(c) << " ";
    std::cout << "\ns2: ";
    for (const char& c : s2)
        std::cout << hex(c) << " ";

    return 0;

以下是我运行此命令时在 Windows 和 Linux 上为 s1 和 s2 打印的十六进制值:

s1(Windows):f0 9f 8e 82 s1 (Linux): f0 9f 8e 82 s2 (Windows): c3 b0 c5 b8 c5 bd e2 80 9a s2 (Linux): f0 9f 8e 82

???? 的 utf8 十六进制值是 f0 9f 8e 82,所以除了 Windows 上的 s2 之外,一切都如预期的那样。谁能解释一下?

【问题讨论】:

看起来像是utf8字符串的双重utf8转换。 是的。字符 U+00F0 以 UTF-8 编码,字节 0xC3 0xB0。您的编译器将 u8 字符串视为 CP-1252 或 Latin-1 或其他单字节字符编码的内容,并将其转换为 UTF-8。 在我看来,UTF-8 支持在 Linux 上的 g++ 中不再是问题,但在 Windows 上的 VS 中仍然是一个问题。对我有用的东西(不管每个系统):将 UTF-8 序列编码为八进制序列:例如"\303\260" 用于 U+00F0(并且默默地假设 std::string 永远不会包含除 UTF-8 之外的任何内容)。这在过去和现在都有效。 (但是,我年纪大了,不灵活地采用新功能......);-) 尝试将源文件保存为带有 BOM 的 UTF-8。如果没有 BOM,编译器将采用系统默认代码页。或者,对于足够新的 MSVC 版本,使用 `/utf-8' option 【参考方案1】:

Microsoft 编译器假定源代码是 ANSI 编码的,这取决于所使用的 Windows 的本地化版本。在美国和西欧 Windows 上,编码假定为 Windows-1252

当编译器假定Windows-1252 时,它会解码源中以错误编码编码的UTF-8 字节并认为它是四个Windows-1252 字符,然后将那些 字符编码为UTF- 8.快速演示(Python):

>>> '?'.encode('utf8') # bytes in the file
b'\xf0\x9f\x8e\x82'
>>> b'\xf0\x9f\x8e\x82'.decode('Windows-1252') # What the compiler reads.
'🎂'
>>> '🎂'.encode('utf8') # What the compiler generates for u8 string.
b'\xc3\xb0\xc5\xb8\xc5\xbd\xe2\x80\x9a'

要使用 UTF-8 源代码,有两个选项是使用带有 BOM 的 UTF-8 对源代码进行编码,或者添加 /utf-8 编译器开关。

【讨论】:

感谢您的回答。只是继续讨论一下。因此,在这种情况下,编译器假设 u8-literals 是 Windows-1252 编码的,但正确地假设所有普通字符串文字都是 utf8 编码的。因此,如果文档在 Windows 上是 utf8 编码并且使用包含 utf8 字符的文字,则假定正确的编码,但如果您想使用 u8 文字说明符,则需要 /utf-8 编译器标志。似乎是一个错误,编译器标志只是在规避它。 @aatwo 不,在您的示例中,s1 将包含源中编码的字节。编译器在读取源代码时采用编码,并在创建"..." 字符串时采用相同的编码。 L"..."u8"..." 字符串必须使用假定或指定的源编码将源字节解码为 Unicode 代码点,然后将这些代码点分别编码为 UTF-16/UTF-32 (Windows/Linux) 或 UTF-8 ,因此如果源编码不正确,则会得到不正确的结果。

以上是关于C++ u8 文字 - Windows 上的意外编码的主要内容,如果未能解决你的问题,请参考以下文章

patchValue 错误上的 Angular PrimeNG p-calendar 反应形式 位置 2 处的意外文字

内部 C++ 中的字符编码?

Windows 10 上的 vs 代码中的“liveSass.command.watchMySass 未找到”和“扩展主机意外终止”

在展开可选值时意外发现 nil

C++中u8R“delim(SomeTextInHere)delim”是啥意思?

怎样用Beyond Compare进行代码的合并与对比