C++ 为什么unicode字符在std::string中存储为UTF-8,在wchar\u t中存储为UTF-16/32?
我有一小段代码:C++ 为什么unicode字符在std::string中存储为UTF-8,在wchar\u t中存储为UTF-16/32?,c++,unicode,utf-8,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
#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;
}
我不明白两件事
tc.c_str()
(确切地说,它的[0]
、[1]
和[2]
索引)打印为UTF-8,看起来像UTF-16/32,前导FF字节wchar\u t
变量会根据是否使用L
前缀给出不同的输出,即使用它似乎会产生UTF-16/32内容,而UTF-8没有L
前缀,这是为什么char
是有符号的或无符号的
,具体取决于编译器。该标准没有规定默认类型,它是编译器供应商的选择
将char
传递到print()
会将调用堆栈上的值从8位扩展到32位。然后%x
打印该32位值的位,默认情况下忽略前导零(除非在%x
上使用长度说明符来保留它们)。8位值如何扩展到32位取决于其实际类型
在您的例子中,您看到的额外的f
s是由于char
值是符号扩展的。0xEx
、0x8x
和0xAx
的高位均为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()
的用法,在您的示例中不需要它)
“€”和“€”
的解释取决于源文件保存为的编码以及编译器配置为在其中操作的编码
如果源代码文件保存在UTF-8中(要强制使用UTF-8文本,可以在C++11及更高版本中使用u8
前缀),则无前缀的“€”和“€”文本可以在UTF-8中。以不同的编码保存文件,您将看到不同的结果。然后将该解释的结果按原样分配给tc
,并在widec
中按原样编码为wchar\t
另一方面,L
前缀迫使编译器将L'€'
解释为宽文本而不是窄文本,因此不存在如何解释它的问题。它知道文本是Unicode,因此在将其编码为wchar\u t
值之前,它先确定Unicode码点值(wchar\u t
在Windows上是16位,在其他平台上是32位),然后在widecl
中进行编码。€
的Unicode码点为
“它知道文字是Unicode”标准中没有要求宽字符串使用Unicode字符集。
L'€'
的结果是实现定义的。如果需要UTF-16,则必须使用u'€'
,而对于UTF-32,则必须使用u'€'
。字符太小,无法作为UTF-16或32的编码元素。他们没有确定wchar_t的大小,这让wchar_t陷入了困境,后来不得不添加char16_t和char32_t。
printf("printf as hex - std::string tc(\"€\") = %x %x %x\n",
(unsigned char) tc[0], (unsigned char) tc[1], (unsigned char) tc[2]);