Unicode文字 - 这甚至有意义吗?

时间:2016-12-12 18:13:55

标签: c++ unicode character-encoding

int main() {    
    std::cout << "\u2654" << std::endl; // Result #1: ♔
    std::cout << U'\u2654' << std::endl; // Result #2: 9812
    std::cout << U'♔' << std::endl; // Result #3: 9812
    return 0;
}

我无法理解Unicode如何使用C ++。为什么文字不输出终端中的文字?

我有点想要这样的事情发挥作用;

char32_t txt_representation() { return /* Unicode codepoint */; } 

注意:源是UTF-8,终端,坐在macOS Sierra,CLion。

4 个答案:

答案 0 :(得分:7)

C ++在其类型系统中并没有真正的“字符”概念。 charwchar_tchar16_tchar32_t都被认为是整数的种类。因此,'x'L'x'U'x'等字符文字都是数字operator<<专门针对char,这就是

的原因
cout << "endl is almost never necessary" << '\n';

做同样的事情
cout << "endl is almost never necessary\n";

但是*char_t没有类似物,所以你的宽字符文字被静默转换为int并打印出来。我个人从不使用iostream,因此我实际上并不知道如何说服operator<<打印一个数字作为其Unicode代码点,但可能有一些方法可以做到。

类型系统中“字符串”和“整数数组”之间存在更强的区别,因此在提供字符串文字时,您可以获得预期的输出。但请注意,cout << L"♔"不会提供您期望的输出,并且cout << "♔"甚至不能保证编译。 cout << u8"♔" 将在符合C ++ 11标准的系统上工作,其中窄字符编码实际上是UTF-8,但如果字符编码是其他内容,则可能会产生mojibake。

(是的,这有点复杂,没有任何借口可以存在。这部分是因为从C继承的向后兼容性约束,部分是因为它在1990年代之前设计,在Unicode接管之前这个世界,部分是因为C ++字符串和流类中的许多设计错误都没有明显的错误,直到修复它们为时已晚。)

答案 1 :(得分:2)

不支持将宽字符打印到窄流,并且根本不起作用。 (它“有效”但结果不是你想要的。)

不支持将多字节窄字符串打印到宽流,并且根本不起作用。 (它“有效”但结果不是你想要的。)

在支持Unicode的系统上,std::cout << "\u2654"按预期工作。 std::cout << u8"\u2654"也是如此。最适当设置基于Unix的操作系统是准备好Unicode的。

在支持Unicode的系统上,如果正确设置程序区域设置,std::wcout << L'\u2654'应该按预期工作。这是通过以下呼叫完成的:

 ::setlocale(LC_ALL, "");

或者

 ::std::locale::global(::std::locale(""));

注意“应该”;对于某些编译器/库,此方法可能根本不起作用。这些编译器/库是一个缺陷。我在看你,libc ++。它可能是也可能不是正式的错误,但我将其视为一个错误。

您应该在所有希望使用Unicode的程序中设置您的语言环境,即使这似乎没有必要。

在同一程序中混用coutwcout不起作用且不受支持。

std::wcout << U'\u2654' 有效,因为这会将wchar_t流与char32_t字符混合在一起。 wchar_tchar32_t是不同的类型。我想正确设置std::basic_stream<char32_t>可以使用char32_t字符串,标准库不会提供任何字符串。

基于

char32_t的字符串适用于存储和处理Unicode代码点。不要将它们直接用于格式化输入和输出。 std::wstring_convert可用于来回转换它们。

TL; DR适用于std::streamstd::string s,或者(如果您不在libc ++上)std::wstreamstd::wstring s。

答案 2 :(得分:2)

Unicode和C ++

有几种unicode编码:

  • UTF-8将每个unicode字符编码为一到四(8位)字节的序列(char
  • UTF-16(可以是BE和LE,取决于字节顺序)将每个unicode字符编码为一个或两个16位字(char16_t)的序列。
  • UTF-32(再次是BE或LE)将每个unicode字符编码为一个32位字(char32_t)。

以下是James McNellis的 excellent video tutorial on unicode with C++ 。他解释了你需要知道的关于字符集编码,unicode及其不同编码以及如何在C ++中使用它的所有内容。

您的代码

"\u2654"是一个狭窄的字符串文字,其类型数组为charwhite chess king unicode character将被编码为与UTF-8编码({ 0xe2, 0x99, 0x94 })对应的3个连续字符。因为我们在一个字符串中,没有问题,其中有几个字符。由于您的控制台区域设置当然使用UTF8,因此它将在显示字符串时正确解释序列。

U'\u2654'char32_t类型的字符文字(因为大写的U)。因为它是char32_t(而不是char),所以它不会显示为char,而是显示为整数值。十进制的值是9812.如果你使用十六进制,你会立即认出它。

最后U'♔'遵循相同的逻辑。但请注意,您在源代码中嵌入了一个unicode字符。只要编辑器的字符编码与编译器期望的源代码编码匹配,这就没问题。但是,如果文件被复制(没有转换)到期望不同编码的环境,这可能会导致不匹配。

答案 3 :(得分:1)

在我的系统上,我无法使用std::coutstd::wcout混合,并获得明智的结果。所以你必须分开做这些。

您应该使用std::locale::global(std::locale(""));区域设置设置为本机系统的区域设置

另外使用宽流作为后两个输出

或者:

std::locale::global(std::locale(""));

std::cout << "\u2654" << std::endl;

或者:

std::locale::global(std::locale(""));

std::wcout << L"\u2654" << std::endl;
std::wcout << L'♔' << std::endl;

这应该鼓励输出流在本地系统的编码和utf8(第一个例子)或ucs16/utf32(第二个例子)之间进行转换。

我认为最安全的第一个例子(编辑可以有其他编码)最好在字符串前面添加u8

std::cout << u8"\u2654" << std::endl;