我正在用C ++编写一个跨平台的应用程序。所有字符串在内部都是UTF-8编码的。考虑以下简化代码:
#include <string>
#include <iostream>
int main() {
std::string test = u8"Greek: αβγδ; German: Übergrößenträger";
std::cout << test;
return 0;
}
在Unix系统上,std::cout
期望8位字符串采用UTF-8编码,因此此代码可以正常工作。
但是,在Windows上,std::cout
期望8位字符串采用Latin-1或类似的非Unicode格式(取决于代码页)。这将导致以下输出:
希腊文:╬▒╬▓╬│╬┤; 德语:├£bergr├Â├ƒentr├ñger
std::cout
在Windows上我该怎么做才能将8位字符串解释为UTF-8?
这是我尝试的:
#include <string>
#include <iostream>
#include <io.h>
#include <fcntl.h>
int main() {
_setmode(_fileno(stdout), _O_U8TEXT);
std::string test = u8"Greek: αβγδ; German: Übergrößenträger";
std::cout << test;
return 0;
}
我希望那_setmode
能达到目的。但是,这导致在调用的行中出现以下断言错误operator<<
:
Microsoft Visual C ++运行时库
调试断言失败!
程序:d:\ visual studio 2015 \ Projects \ utf8test \ Debug \ utf8test.exe文件:minkernel \ crts \ ucrt \ src \ appcrt \ stdio \ fputc.cpp行:47
表达式:((_Stream.is_string_backed())||(fn = _fileno(_Stream.public_stream()),((__textmode_safe(fn)== __crt_lowio_text_mode :: ansi)&&!_tm_unicode_safe(fn))))
有关程序如何导致断言失败的信息,请参见有关断言的Visual C ++文档。
问题不std::cout
只是Windows控制台。使用C-标准输入输出,你将获得ü
与fputs( "\xc3\xbc", stdout );
设置UTF-8代码页后(使用SetConsoleOutputCP
或chcp
),并设置在cmd中的设置一个Unicode支持字体(索拉应该支持超过2000个字符,并有注册表黑客以更强大的字体到CMD) 。
如果在一个字节之后一个字节输出,putc('\xc3'); putc('\xbc');
则会得到双豆腐,因为控制台会将它们分别解释为非法字符。这可能就是C ++流所做的。
有关冗长的讨论,请参见Windows控制台上的UTF-8输出。
对于我自己的项目,我最终实现了std::stringbuf
向Windows-1252的转换。我确实需要完整的Unicode输出,但是,这并不能真正帮助你。
另一种方法是覆盖cout
的streambuf,fputs
用于实际输出:
#include <iostream>
#include <sstream>
#include <Windows.h>
class MBuf: public std::stringbuf {
public:
int sync() {
fputs( str().c_str(), stdout );
str( "" );
return 0;
}
};
int main() {
SetConsoleOutputCP( CP_UTF8 );
setvbuf( stdout, nullptr, _IONBF, 0 );
MBuf buf;
std::cout.rdbuf( &buf );
std::cout << u8"Greek: αβγδ\n" << std::flush;
}
我在这里关闭了输出缓冲,以防止它干扰未完成的UTF-8字节序列。
这似乎确实是问题的一部分。如果我
SetConsoleOutputCP(CP_UTF8);
按照Miles的建议使用,并按Paul的建议切换到非光栅字体并使用fputs
代替std::cout
,那么它可以工作!-现在,我需要找出是否有一种方法可以std::cout
正确表现。我认为没有办法。并且
fputs
也不保证能正常工作,请参见我的双重putc
示例。您可以尝试理解UTF-8(将字符保持在一起)并使用来更改cout
的内容streambuf
(请参阅rdbuf()
参考资料)fputs
。我发现可以通过启用缓冲来解决此问题。看我的答案。感谢您指出正确的方向!
关于您的编辑:恐怕它对我不起作用。我收到“希腊语:╬▒╬▓╬│╬┤”的字样。
您是在Visual Studio中进行测试吗?我注意到,这仅可直接从cmd实例启动程序。