为什么unicode char在std :: string中存储为UTF-8,在wchar_t中存储为UTF-16/32?

时间:2017-04-18 19:17:32

标签: c++ unicode utf-8

我有一小段代码:

#include <locale.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>

wchar_t widec('€');
wchar_t widecl(L'€');
std::string tc("€");

int main(int argc, char *argv[])
{
    printf("printf as hex - std::string tc(\"€\") = %x %x %x\n\r", tc.c_str()[0], tc.c_str()[1], tc.c_str()[2]);
    printf("printf as hex - wchar_t widec('€') = %x\n\r", widec);
    printf("printf as hex - wchar_t widecl(L'€') = %x\n\r", widecl);

    return 0;
}

输出:

printf as hex - std::string tc("€") = ffffffe2 ffffff82 ffffffac
printf as hex - wchar_t widec('€') = e282ac
printf as hex - wchar_t widecl(L'€') = 20ac

我不明白两件事。

  1. 为什么tc.c_str()(其[0][1][2]索引准确无误)打印为UTF-8,看起来像UTF-16/32领先FF字节?

  2. 为什么初始化相同的wchar_t变量会产生不同的输出,具体取决于是否使用L前缀,即。使用它似乎产生UTF-16/32内容和没有L前缀的UTF-8,为什么会这样?

1 个答案:

答案 0 :(得分:1)

  1. 没有显式符号说明符的charsignedunsigned,具体取决于编译器。该标准没有规定默认类型,它是编译器供应商的选择。

    char传递给print()会将值从8位扩展到32位。然后%x打印该32位值的位,默认忽略前导零(除非您在%x上使用长度说明符来保留它们)。 8位值如何扩展到32位取决于它的实际类型。

    在您的情况下,您看到的额外f是由于char值为符号扩展0xEx0x8x0xAx的高位均为1,因此1用于在扩展期间填充高24位。这意味着您的编译器将char实现为signed类型,并将值扩展为signed int。您可以手动将char值类型转换为unsigned,以强制它们零扩展

    printf("printf as hex - std::string tc(\"€\") = %x %x %x\n",
           (unsigned char) tc[0], (unsigned char) tc[1], (unsigned char) tc[2]);
    

    (请注意,我删除了c_str()的使用,在您的示例中没有必要)

  2. '€'"€"没有任何前缀的解释取决于源文件保存的编码,以及编译器配置为运行的编码。

    无前缀'€'"€"文字可以是UTF-8的唯一方法是,如果您的源代码文件以UTF-8保存(强制使用UTF-8文字,您可以在C ++ 11及更高版本中使用u8前缀。以不同的编码保存文件,您将看到不同的结果。然后,该解释的结果按原样分配给tc,并按wchar_t编码为widec

    另一方面,L前缀迫使编译器将L'€'解释为宽文字而不是狭义文字,因此不应该如何解释它。它知道文字是Unicode,因此它确定了Unicode代码点值,然后将其编码为wchar_t值(wchar_t在Windows上为16位,在其他平台上为32位)在{ {1}}。 widecl的Unicode代码点为U+20AC EURO SIGN