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时,输出将更改为 ,这意味着

  • 字符ü可以使用fputs及其UTF-8编码C3 BC编写
  • std::cout不管用什么原因
  • 在尝试写入字符后,流failbit正在设置
  • Windows 7:使用Consolas的输出是 。 更有趣。 正确的字节被写入,可能(至少在将输出重定向到文件时)和流状态正常,但两个字节被写为单独的字符)。

    我试图在“微软连接”(见这里)中提出这个问题,但MS并没有太大的帮助。 你可能会在这里看到类似的问题。

    你能重现这个问题吗?

    我究竟做错了什么? 不应该std::coutfputs有相同的效果?

    已解决:(有点)以下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并发送至控制台。 目前没有发现任何问题。

    另请注意,此解决方案不需要修改任何代码页,无论是SetConsoleCPSetConsoleOutputCP还是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;
    
        // ...
    }
    

    使用WideCharToMultiByteMultiByteToWideChar WinAPI函数可以很容易地实现wstring_to_utf8utf8_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 ++流在这里所做的。

    链接地址: http://www.djcxy.com/p/43599.html

    上一篇: 8 output on Windows console

    下一篇: Returning data from database in .net: Return a DataTable or LIst<T>?