1. 程式人生 > >MinGW-W64使得printf、cout、wprintf、wcout顯示出中文的種種

MinGW-W64使得printf、cout、wprintf、wcout顯示出中文的種種

使用MinGW-W64-gcc 4.9.2 的64位 posix執行緒 seh異常機制的編譯器。它內建了iconv支援,可以在UTF-8與gbk之間來回轉碼,這個很重要。

說明:printf、cout、wprintf、wcout這些如何輸出中文,C/C++語言標準完全沒提其實現細節。所以這些是針對具體的編譯器環境而不同的。MinGW-W64、Linux GCC、Microsoft Visual C++都有所出入。

1  為使得printf顯示出中文,僅需要

gcc 命令列選項-fexec-charset=gbk

這意味著編譯器生成的可執行檔案的字串的內容都是按照gbk編碼的。在Windows環境下呼叫相應的C的stdout,正確顯示毫無問題。

2  為使得cout顯示出中文,僅需要

gcc 命令列選項-fexec-charset=gbk

這意味著cout應該是完全基於C的stdout實現。


3    使wprintf能輸出漢字,需要且僅需要這一行:

_setmode(_fileno(stdout), _O_WTEXT);            // 需要 #include <io.h> 和 <fcntl.h>

及gcc 命令列選項-fexec-charset=gbk

但是在Visual C++與Linux gcc中,都不支援上述_setmode函式,雖然這兩個編譯器已經充分實現了wprintf、wcout輸出中文。

但是,上述_setmode語句會造成wcout輸出中文與英文字元時一片亂碼!

可見,wprintf是使用了C語言的標準輸出stdout。僅此而已。至於stdout應該採取的預設編碼字符集,由gcc 命令列選項-fexec-charset=gbk指定即可。

4  為使得wcout輸出中文,僅需要下述2條語句:

setlocale(LC_ALL, "");                          // 需要 #include <locale>

std::ios_base::sync_with_stdio(false);       // 缺少的話,wcout輸出的 wchar_t字串會漏掉中文字元

    /*  ios_base::sync_with_stdio 決定 C++ 標準 streams (cin,cout,cerr...) 是否
    *  與相應的 C 標準程式庫檔案 (stdin,stdout,stderr) 同步,
    *  也就是是否使用相同的 stream 緩衝區,預設情況是同步的。
    *  但由於同步會帶來某些不必要的負擔,因此該函式作用就是我們自己可以取消同步
    *  std::ios::sync_with_stdio(false);
    *  注意:必須在任何 io 操作之前取消同步
    */

完全不需要  std::wcout.imbue(std::locale(""));           // 使 wcout 使用操作系環境中預設的 locale

由此可見,wcout是獨立於C的標準輸出stdout而獨立實現的;且在把寬字元轉換為作業系統預設字元編碼時,使用了C語言標準的locale設定(即setlocale函式),沒有setlocale函式的設定,僅靠gcc命令列選項gcc 命令列選項-fexec-charset=gbk是不行的。

僅此而已。

5  示例程式

#include <iostream>
#include <locale>
#include <cstdio>
#include <io.h>
#include <fcntl.h>

int main(void)
{
    printf("printf的內容\n");

    std::cout << "cout 的內容" << std::endl;

    
    //fflush(stdout); //must be done before _setmode  實測不需要這行
    //_setmode(_fileno(stdout), _O_WTEXT);            // 需要 #include <io.h> 和 <fcntl.h>
    wchar_t t[] = L"pure english\n";
    wprintf(L"wprintf: %ls", t);
    wprintf(L"wprintf: C 寬字元字串\n");


    //setlocale(LC_ALL, "");                          // 需要 #include <locale>
    //std::ios_base::sync_with_stdio(false);       // 缺少的話,wcout wchar_t 會漏掉中文
    /*  ios_base::sync_with_stdio 決定 C++ 標準 streams (cin,cout,cerr...) 是否
    *  與相應的 C 標準程式庫檔案 (stdin,stdout,stderr) 同步,
    *  也就是是否使用相同的 stream 緩衝區,預設情況是同步的。
    *  但由於同步會帶來某些不必要的負擔,因此該函式作用就是我們自己可以取消同步
    *  std::ios::sync_with_stdio(false);
    *  注意:必須在任何 io 操作之前取消同步
    */
    //std::wcout.imbue(std::locale(""));           // 使 wcout 使用客戶環境中預設的 locale

    std::wcout << L"wcout: pure english" << std::endl;
    std::wstring u = L"wcout: C++ 寬字元版 string";
    std::wcout << L"wstring u 的內容是:" << u << std::endl;
    std::wcout << L"wstring u 的字元數為:" << u.size() << std::endl;

    return 0;

}


ps.

On Windows, the only way to achieve direct Unicode output is via WriteConsoleW(). The MS CRT (2008 and newer) provides a way to use C/C++ I/O facilities for direct Unicode output:

Code: ?
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <fcntl.h> #include <io.h> #include <cstdio> #include <cwchar> #include <iostream> using namespace std; int main() { _setmode(_fileno(stdout), _O_U16TEXT); fputws(L"\u00e7\n", stdout); wcout << L"\u00e7" << endl; return 0; }//main
This will send the UTF16LE string directly to WriteConsoleW(), unless output is redirected to a file, in which case the UTF16LE string is written as a stream of bytes via WriteFile().