2016-11-30 55 views
1

我想讓std::cout打印一個UTF-8文字。這對於gcc來說似乎是一件容易的事情,但對於Windows來說這是一件非常困難的事情。std :: cout可以在Windows上使用UTF-8嗎?

,我試圖去工作的代碼是:

std::cout << "Ελληνικά Русский 你好"; 

環境:

  • 的Windows 10時,Visual Studio 2015年
  • 默認編碼:1251
  • 控制檯編碼:866
  • 源編碼:帶BOM的UTF-8

要求:

  • 沒有改變的代碼行本身必須進行
  • 完整的Unicode範圍支持
  • 一些設置代碼可以在開始時加入main()

我試過的:

  • #pragma execution_character_set("utf-8")
  • SetConsoleCP(CP_UTF8); SetConsoleOutputCP(CP_UTF8);
  • 設置控制檯字體爲Lucida Console全系統
  • Use Unicode character set在項目屬性從this博客

沒有什麼幫助,而沒有StackOverflow的答案

  • 設置代碼解決了這個問題。

    編輯

    爲了得到統一部分工作,請執行下列操作:在項目設置上Use Unicode Character Set

    • 呼叫initStreams()從下面的清單在開始
    • 打開
    • 添加/utf-8選項

    不工作:

    • wprintf
    • cin/wcin
    • 中國文字

    initStreams()實現:

    #include <cassert>   // assert 
    #include <codecvt>   // std::codecvt_utf8 (C++11) 
    #include <stdexcept>  // std::exception 
    #include <streambuf>  // std::basic_streambuf 
    #include <iostream>   // std::cout, std::endl 
    #include <locale>   // std::locale 
    #include <memory>   // std::unique_ptr (C++11) 
    
    #undef UNICODE 
    #define UNICODE 
    #undef STRICT 
    #define STRING 
    #include <windows.h> // MultiByteToWideChar 
    
    class OutputForwarderBuffer : public std::basic_streambuf<char> 
    { 
    public: 
        using Base = std::basic_streambuf<char>; 
        using Traits = Base::traits_type; 
        using StreamBuffer = std::basic_streambuf<char>; 
        using WideStreamBuffer = std::basic_streambuf<wchar_t>; 
        using Base::int_type; 
        using Base::char_type; 
    
        OutputForwarderBuffer(
         StreamBuffer& existingBuffer, 
         WideStreamBuffer* pWideStreamBuffer 
        ) 
         : Base(existingBuffer) 
         , pWideStreamBuffer_(pWideStreamBuffer) 
        { 
        } 
    
        OutputForwarderBuffer(OutputForwarderBuffer const&) = delete; 
        void operator=(OutputForwarderBuffer const&) = delete; 
    
    protected: 
        std::streamsize xsputn(char const* s, std::streamsize n) override 
        { 
         if (n == 0) { return 0; } 
    
         int const sourceSize = static_cast<int>(n); 
         int const destinationSize = MultiByteToWideChar(CP_UTF8, 0, s, sourceSize, nullptr, 0); 
         wideCharBuffer_.resize(static_cast<size_t>(sourceSize)); 
    
         int const nWideCharacters = MultiByteToWideChar(CP_UTF8, 0, s, sourceSize, &wideCharBuffer_[0], destinationSize); 
         assert(nWideCharacters > 0 && nWideCharacters == destinationSize); 
    
         return pWideStreamBuffer_->sputn(&wideCharBuffer_[0], destinationSize); 
        } 
    
        int_type overflow(int_type c) override 
        { 
         bool const cIsEOF = Traits::eq_int_type(c, Traits::eof()); 
         int_type const failureValue = Traits::eof(); 
         int_type const successValue = (cIsEOF ? Traits::not_eof(c) : c); 
    
         if (!cIsEOF) { 
          char_type const ch = Traits::to_char_type(c); 
          std::streamsize const nCharactersWritten = xsputn(&ch, 1); 
    
          return (nCharactersWritten == 1 ? successValue : failureValue); 
         } 
         return successValue; 
        } 
    
    private: 
        WideStreamBuffer* pWideStreamBuffer_; 
        std::wstring wideCharBuffer_; 
    }; 
    
    void setUtf8Conversion(std::basic_ios<wchar_t>& stream) 
    { 
        stream.imbue(std::locale(std::locale::empty(), new std::codecvt_utf8_utf16<wchar_t>())); 
    } 
    
    bool isConsole(HANDLE streamHandle) 
    { 
        DWORD consoleMode; 
        return !!GetConsoleMode(streamHandle, &consoleMode); 
    } 
    
    bool isConsole(DWORD stdStreamId) 
    { 
        return isConsole(GetStdHandle(stdStreamId)); 
    } 
    
    void initStreams() 
    { 
        SetConsoleCP(CP_UTF8); 
        SetConsoleOutputCP(CP_UTF8); 
    
        setUtf8Conversion(std::wcout); 
        setUtf8Conversion(std::wcerr); 
        setUtf8Conversion(std::wclog); 
    
        static OutputForwarderBuffer coutBuffer(*std::cout.rdbuf(), std::wcout.rdbuf()); 
        static OutputForwarderBuffer cerrBuffer(*std::cerr.rdbuf(), std::wcerr.rdbuf()); 
        static OutputForwarderBuffer clogBuffer(*std::clog.rdbuf(), std::wclog.rdbuf()); 
    
        std::cout.rdbuf(&coutBuffer); 
        std::cerr.rdbuf(&cerrBuffer); 
        std::clog.rdbuf(&clogBuffer); 
    } 
    
  • +1

    你試過用'wcout'嗎? 'std :: wcout << L「ΕλληνικάРусский你好」;'? –

    +0

    @AlgirdasPreidžius我知道它會工作。我的目標是避免修改那些在Linux上完美運行的代碼。 – Anton3

    +0

    你能更具體地瞭解什麼是行不通的?怎麼了? – aardvarkk

    回答

    0

    這是我會怎麼做:

    1. 確保您的源文件是UTF-8編碼,並有正確的內容(在另一個編輯器中打開它們,檢查字形和文件編碼)

    2. 刪除控制檯 - 將輸出重定向到一個文件並使用utf-8感知編輯器檢查它的內容(就像源代碼一樣)

    3. 使用/ utf-8使用MSVC2015的cmdline選項 - 這將強制編譯器處理所有源文件都以utf-8編碼一次,並且存儲在生成的二進制文件中的字符串文字將爲b utf-8編碼。

    4. 刪除iostreams的從公式(不能等到該庫的死,TBH) - 使用cstdio

    5. 此時輸出應工作(它確實對我來說)

    6. 讓控制檯輸出工作 - 使用SetConsoleOutputCP(CP_UTF8)並讓它使用支持您的Unicode平面的TrueType字體(我懷疑漢字在控制檯上工作時需要在系統中安裝字體,支持相關Unicode的平面和您的控制檯應該被配置爲使用它)

    7. 不能確定控制檯輸入(從來沒有處理這個),但我懷疑SetConsoleCP(CP_UTF8)應使其與非寬我的工作/ o

    8. 放棄使用寬I/O(wcout/etc)的想法 - 爲什麼你會這樣做呢?Unicode的作品就好用UTF-8編碼字符常量*

    9. 一旦你達到了這個階段 - 的時間來處理輸入輸出流(如果你堅持使用它)。我現在不理會wcin/wcout。如果他們還沒有工作 - 嘗試用utf-8語言環境灌輸相關的cin/cout。

    10. http://utf8everywhere.org/提出的想法是僅在您進行Windows API調用時才轉換爲UCS-2。這使得您的OutputForwarderBuffer不必要。

    11. 我想(如果你真的堅持)現在你可以嘗試讓廣泛的iostreams工作。祝你好運,我想你將不得不重新配置控制檯(這將打破非廣泛的I/O)或以某種方式讓你的wcout/wcin在飛行中執行UCS2到UTF8的轉換(並且只有當它連接到控制檯時) 。

    相關問題