8在Windows控制台上输出
以下代码显示了我的计算机上的意外行为(在Windows 7上使用Windows XP和VS 2012上的Visual C ++ 2008 SP1进行了测试):
#include <iostream>
#include "Windows.h"
int main() {
SetConsoleOutputCP( CP_UTF8 );
std::cout << "xc3xbc";
int fail = std::cout.fail() ? '1': '0';
fputc( fail, stdout );
fputs( "xc3xbc", stdout );
}
我简单地用cl /EHsc test.cpp
编译。
Windows XP:在控制台窗口中的输出是ü0ü
(翻译成代码页1252,最初在默认代码页中显示一些线画图字符,也许是437)。 当我更改控制台窗口的设置以使用“Lucida Console”字符集并再次运行我的test.exe时,输出将更改为1ü
,这意味着
ü
可以使用fputs
及其UTF-8编码C3 BC
编写 std::cout
不管用什么原因 failbit
正在设置 Windows 7:使用Consolas的输出是 0ü
。 更有趣。 正确的字节被写入,可能(至少在将输出重定向到文件时)和流状态正常,但两个字节被写为单独的字符)。
我试图在“微软连接”(见这里)中提出这个问题,但MS并没有太大的帮助。 你可能会在这里看到类似的问题。
你能重现这个问题吗?
我究竟做错了什么? 不应该std::cout
和fputs
有相同的效果?
已解决:(有点)以下mike.dld的想法我实现了std::stringbuf
stringbuf在sync()
中将UTF-8转换为Windows-1252,并用此转换器替换了std::cout
的streambuf(请参阅我的评论mike.dld的答案)。
我明白这个问题很老,但如果有人仍然有兴趣,下面是我的解决方案。 我已经实现了一个非常简单的std :: streambuf后代,然后在程序执行开始时将它传递给每个标准流。
这使您可以在程序中的任何地方使用UTF-8。 输入时,数据从控制台以Unicode编码,然后转换并以UTF-8格式返回给您。 在输出上,完成了相反的操作,以UTF-8格式从您处获取数据,将其转换为Unicode并发送至控制台。 目前没有发现任何问题。
另请注意,此解决方案不需要修改任何代码页,无论是SetConsoleCP
, SetConsoleOutputCP
还是chcp
或其他。
这是流缓冲区:
class ConsoleStreamBufWin32 : public std::streambuf
{
public:
ConsoleStreamBufWin32(DWORD handleId, bool isInput);
protected:
// std::basic_streambuf
virtual std::streambuf* setbuf(char_type* s, std::streamsize n);
virtual int sync();
virtual int_type underflow();
virtual int_type overflow(int_type c = traits_type::eof());
private:
HANDLE const m_handle;
bool const m_isInput;
std::string m_buffer;
};
ConsoleStreamBufWin32::ConsoleStreamBufWin32(DWORD handleId, bool isInput) :
m_handle(::GetStdHandle(handleId)),
m_isInput(isInput),
m_buffer()
{
if (m_isInput)
{
setg(0, 0, 0);
}
}
std::streambuf* ConsoleStreamBufWin32::setbuf(char_type* /*s*/, std::streamsize /*n*/)
{
return 0;
}
int ConsoleStreamBufWin32::sync()
{
if (m_isInput)
{
::FlushConsoleInputBuffer(m_handle);
setg(0, 0, 0);
}
else
{
if (m_buffer.empty())
{
return 0;
}
std::wstring const wideBuffer = utf8_to_wstring(m_buffer);
DWORD writtenSize;
::WriteConsoleW(m_handle, wideBuffer.c_str(), wideBuffer.size(), &writtenSize, NULL);
}
m_buffer.clear();
return 0;
}
ConsoleStreamBufWin32::int_type ConsoleStreamBufWin32::underflow()
{
if (!m_isInput)
{
return traits_type::eof();
}
if (gptr() >= egptr())
{
wchar_t wideBuffer[128];
DWORD readSize;
if (!::ReadConsoleW(m_handle, wideBuffer, ARRAYSIZE(wideBuffer) - 1, &readSize, NULL))
{
return traits_type::eof();
}
wideBuffer[readSize] = L' ';
m_buffer = wstring_to_utf8(wideBuffer);
setg(&m_buffer[0], &m_buffer[0], &m_buffer[0] + m_buffer.size());
if (gptr() >= egptr())
{
return traits_type::eof();
}
}
return sgetc();
}
ConsoleStreamBufWin32::int_type ConsoleStreamBufWin32::overflow(int_type c)
{
if (m_isInput)
{
return traits_type::eof();
}
m_buffer += traits_type::to_char_type(c);
return traits_type::not_eof(c);
}
用法如下:
template<typename StreamT>
inline void FixStdStream(DWORD handleId, bool isInput, StreamT& stream)
{
if (::GetFileType(::GetStdHandle(handleId)) == FILE_TYPE_CHAR)
{
stream.rdbuf(new ConsoleStreamBufWin32(handleId, isInput));
}
}
// ...
int main()
{
FixStdStream(STD_INPUT_HANDLE, true, std::cin);
FixStdStream(STD_OUTPUT_HANDLE, false, std::cout);
FixStdStream(STD_ERROR_HANDLE, false, std::cerr);
// ...
std::cout << "xc3xbc" << std::endl;
// ...
}
使用WideCharToMultiByte
和MultiByteToWideChar
WinAPI函数可以很容易地实现wstring_to_utf8
和utf8_to_wstring
。
爱。 恭喜您找到一种方法,可以从程序中更改控制台的代码页。 我不知道那个电话,我总是不得不使用chcp。
我猜C ++默认语言环境正在涉足。 默认情况下,它将使用GetThreadLocale()提供的代码页来确定非wstring内容的文本编码。 这通常默认为CP1252。 你可以尝试使用SetThreadLocale()来获得UTF-8(如果它甚至这样做,无法回想),希望std :: locale默认可以处理你的UTF-8编码。
现在是时候关闭它了。 Stephan T. Lavavej说这种行为是“按设计”的,尽管我不能按照这个解释。
我目前的知识是:UTF-8代码页中的Windows XP控制台不支持C ++ iostreams。
Windows XP现在已经过时了,VS 2008也是如此。我很想知道新的Windows系统是否仍然存在问题。
在Windows 7上 ,效果可能是由于C ++流输出字符的方式。 正如在Windows控制台中正确打印utf8字符的答案中所看到的那样,在打印一个字节之后,如putc('xc3'); putc('xbc');
,UTF-8输出将失败,并显示C stdio putc('xc3'); putc('xbc');
putc('xc3'); putc('xbc');
以及。 也许这就是C ++流在这里所做的。
上一篇: 8 output on Windows console
下一篇: Returning data from database in .net: Return a DataTable or LIst<T>?