C ++中的Unicode问题,但不是C

我正在尝试在Windows上用C ++编写unicode字符串到屏幕上。 我将控制台字体更改为Lucida Console ,并将输出设置为CP_UTF8即65001。

我运行以下代码:

 #include  //notice this header file.. #include  #include  int main() { SetConsoleOutputCP(CP_UTF8); const char text[] = "Россия"; printf("%s\n", text); } 

打印出来就好了!

但是,如果我这样做:

 #include  //the C++ version of the header.. #include  #include  int main() { SetConsoleOutputCP(CP_UTF8); const char text[] = "Россия"; printf("%s\n", text); } 

它打印:

我不知道为什么..

另一件事是我做的时候:

 #include  #include  int main() { std::uint32_t oldcodepage = GetConsoleOutputCP(); SetConsoleOutputCP(CP_UTF8); std::string text = u8"Россия"; std::cout<<text<<"\n"; SetConsoleOutputCP(oldcodepage); } 

我得到与上面相同的输出(非工作输出)。

std::string上使用printf ,它可以正常工作:

 #include  #include  #include  int main() { std::uint32_t oldcodepage = GetConsoleOutputCP(); SetConsoleOutputCP(CP_UTF8); std::string text = u8"Россия"; printf("%s\n", text.c_str()); SetConsoleOutputCP(oldcodepage); } 

但只有我使用stdio.h而不是cstdio

我有什么想法可以使用std::cout吗? 我怎样才能使用cstdio ? 为什么会这样? cstdio不仅仅是stdio.h的c ++版本吗?

编辑:我刚试过:

 #include  #include  #include  int main() { _setmode(_fileno(stdout), _O_U8TEXT); std::wcout << L"Россия" << std::endl; } 

并且它是有效但只有我使用std::wcoutwide strings 。 我真的想避免使用wide-strings ,到目前为止我看到的唯一解决方案是C-printf :l

所以问题仍然存在……

虽然您已将控制台设置为期望UTF-8输出,但我怀疑您的编译器将字符串文字视为其他字符集。 我不知道为什么C编译器的行为不同。

好消息是C ++ 11包含对UTF-8的一些支持,并且Microsoft已经实现了标准的相关部分。 代码有点毛茸茸,但你要查看std::wstring_convert (转换为UTF-8和来自UTF-8)和标头。

您可以使用这些函数转换为UTF-8,并假设您的控制台期望UTF-8,事情应该正常工作。

就个人而言,当我需要调试这样的东西时,我经常将输出定向到文本文件。 文本编辑器似乎比Windows控制台更好地处理Unicode。 在我的情况下,我经常正确输出代码点,但控制台设置不正确,所以我仍然最终打印垃圾。


我可以告诉你,这在Linux(使用Clang)和Windows(使用GCC 4.7.3和Clang 3.5)中对我有用;你需要在命令行中添加“std = c ++ 11”以使用GCC或Clang进行编译):

 #include  int main() { const char text[] = u8"Россия"; std::printf("%s\n", text); } 

使用Visual C ++(2012,但我相信它也适用于2010),我不得不使用:

 #include  #include  #include  #include  int main() { std::wstring_convert> converter; auto text = converter.to_bytes(L"Россия"); std::printf("%s\n", text.c_str()); } 

如果您的文件编码为UTF-8,您会发现字符串长度为12.从 )运行strlen以查看我的意思。 设置输出代码页将完全按照您的方式打印字节。

编译器看到的内容等同于以下内容:

 const char text[] = "\xd0\xa0\xd0\xbe\xd1\x81\xd1\x81\xd0\xb8\xd1\x8f"; 

将它包裹在一个宽字符串中(特别是wchar_t ),事情并不那么好。

为什么C ++以不同的方式处理它? 我没有丝毫的线索,除了C ++版本底层代码使用的机制可能有点无知(例如std::cout愉快地输出你想要的任何盲目的东西)。 无论原因是什么,显然坚持使用C是最安全的…考虑到微软自己的C编译器甚至无法编译C99代码,这实际上是我意想不到的。

无论如何,我建议不要输出到Windows控制台,如果可能的话,不管是不是Unicode。 文件更加可靠,更不用说麻烦了。

更令人惊讶的是,C实现这里工作比C ++没有。 char只能包含一个字节(数值0-255),因此控制台应该只显示ASCII字符。

C必须在这里为你做一些魔术 – 实际上它猜测你提供的ASCII范围(0-127)之外的这些字节形成一个Unicode(可能是UTF-8)多字节字符。 C ++只显示const char[]数组的每个字节,并且因为单独处理的UTF字节在字体中没有不同的字形,所以它将这些字符放入 。 请注意,您指定了6个字母并获得12个问号。

如果需要,您可以阅读有关UTF-8和ASCII编码的信息,但重点是std::wstringstd::wcout确实是设计用于处理大于字节字符的最佳解决方案。

(如果您根本不使用拉丁字符,则在使用基于char的解决方案(例如const char[]std::string而不是std::wstring时甚至不会节省内存。所有这些西里尔字母都必须无论如何要占用一些空间)。