mkl*_*uwe 7 c++ console windows-xp utf-8 visual-studio-2008
以下代码显示了我的计算机上的意外行为(在Windows XP和Windows 7上的VS 2012上使用Visual C++ 2008 SP1进行了测试):
#include <iostream>
#include "Windows.h"
int main() {
    SetConsoleOutputCP( CP_UTF8 );
    std::cout << "\xc3\xbc";
    int fail = std::cout.fail() ? '1': '0';
    fputc( fail, stdout );
    fputs( "\xc3\xbc", stdout );
}
我简单编译了cl /EHsc test.cpp.
Windows XP:控制台窗口中的输出
 ü0ü(转换为代码页1252,最初显示默认代码页中的一些线条图,可能是437).当我更改控制台窗口的设置以使用"Lucida Console"字符集并再次运行我的test.exe时,输出更改为1ü,这意味着
ü可以使用fputsUTF-8编码来编写字符C3 BCstd::cout 因任何原因都不起作用failbit尝试写字符后,流设置Windows 7:使用Consolas输出??0ü.更有趣.可能写入正确的字节(至少在将输出重定向到文件时)并且流状态正常,但这两个字节被写为单独的字符).
我试图在"Microsoft Connect"(见这里)上提出这个问题,但是MS并没有太大的帮助.你可以在这里看一下 以前曾经问过类似的东西.
你能重现这个问题吗?
我究竟做错了什么?不应该std::cout和fputs有相同的效果?
解决:(排序)继mike.dld的想法,我实现了一个std::stringbuf做从UTF-8转换到Windows 1252中sync(),取而代之的流缓冲std::cout这种转换器(见我的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'\0';
        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 << "\xc3\xbc" << std::endl;
    // ...
}
左明wstring_to_utf8和utf8_to_wstring可以很容易地与实施WideCharToMultiByte和MultiByteToWideCharWinAPI的功能。
现在是时候关闭这个了。Stephan T. Lavavej说这种行为是“设计使然”,尽管我无法理解这个解释。
我目前的知识是:UTF-8 代码页中的 Windows XP 控制台不适用于 C++ iostream。
Windows XP 现在已经过时了,VS 2008 也已经过时了。我很想知道这个问题在较新的 Windows 系统上是否仍然存在。
在 Windows 7 上,这种影响可能是由于 C++ 流输出字符的方式造成的。正如在Properly print utf8characters in windows console的答案中看到的,在一个接一个地打印一个字节时,C stdio 的 UTF-8 输出也会失败putc('\xc3'); putc('\xbc');。也许这就是 C++ 流在这里所做的事情。