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::wcout
和wide 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::wstring
和std::wcout
确实是设计用于处理大于字节字符的最佳解决方案。
(如果您根本不使用拉丁字符,则在使用基于char
的解决方案(例如const char[]
和std::string
而不是std::wstring
时甚至不会节省内存。所有这些西里尔字母都必须无论如何要占用一些空间)。