【发布时间】:2014-02-17 16:37:09
【问题描述】:
我正在尝试在 Windows 上用 C++ 将 unicode 字符串写入屏幕。我将控制台字体更改为Lucida Console,并将输出设置为CP_UTF8 aka 65001。
我运行以下代码:
#include <stdio.h> //notice this header file..
#include <windows.h>
#include <iostream>
int main()
{
SetConsoleOutputCP(CP_UTF8);
const char text[] = "Россия";
printf("%s\n", text);
}
打印出来就好了!
但是,如果我这样做:
#include <cstdio> //the C++ version of the header..
#include <windows.h>
#include <iostream>
int main()
{
SetConsoleOutputCP(CP_UTF8);
const char text[] = "Россия";
printf("%s\n", text);
}
打印:������������
我不知道为什么..
另一件事是当我这样做时:
#include <windows.h>
#include <iostream>
int main()
{
std::uint32_t oldcodepage = GetConsoleOutputCP();
SetConsoleOutputCP(CP_UTF8);
std::string text = u8"Россия";
std::cout<<text<<"\n";
SetConsoleOutputCP(oldcodepage);
}
我得到与上面相同的输出(非工作输出)。
在std::string 上使用printf,但效果很好:
#include <stdio.h>
#include <windows.h>
#include <iostream>
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 <iostream>
#include <io.h>
#include <fcntl.h>
int main()
{
_setmode(_fileno(stdout), _O_U8TEXT);
std::wcout << L"Россия" << std::endl;
}
是的,它有效,但前提是我使用 std::wcout 和 wide strings。我真的很想避免wide-strings,到目前为止我看到的唯一解决方案是C-printf:l
所以问题仍然存在..
【问题讨论】:
-
如果在包含
cstdio的情况下执行std::printf会怎样? -
它打印相同的坏字符。有或没有
std::没有区别我使用的是Mingw 4.8.1。最新版本。 -
我在VS2010中尝试过一次这个实验。结果:不要使用 UTF8。 IIRC 的主要问题是流的缓冲区,即
cout一次将一个字符传递给控制台,然后无法正确呈现多单元代码点。 -
@MarkRansom 是的,你可以用你自己的替换 cout 的缓冲区。我也试过了,它“有效”,但 CRT 本身不完全支持 (*) 并且控制台也不支持 IIRC。 (*) MSDN 说
setlocale不支持每个字符超过两个字节的语言环境。 -
dyp 所说的 - 代码页 65001 已损坏到通常无法使用的程度。多字节编码仅在 MS CRT 中正确支持 ANSI 代码页,如 Windows 在某些东亚语言环境中默认使用的 932 和 936。在内部处理 UTF-8 格式的字符串可能是一件明智的事情,但在 Windows 上,它仍然是一个二等公民,不能与任何标准的面向字节的 C stdlib 接口一起工作。遗憾的是,通常最好使用一层将 Win32 范围的 API 转换为 UTF-8。