關於 wcout 輸出中文的問題
作者:闕榮文
時間:2011/8/16
1. cout
情境1: 在源檔案中定義 const char* str = "中文" 在 VC++ 編譯器上,由於Windows環境用 GBK編碼,所以字串 "中文" 被儲存為 GBK內碼,
編譯器也把 str 指向一個包含有 GBK編碼的唯讀記憶體空間.
用 cout 輸出 str 時, 由於中文Windows環境用GBK編碼,所以把GBK編碼的 str 內容輸出到控制台,沒問題.
情境2: 在Linux 下編輯一個檔案 const char* str = "中文", 由於Linux普遍使用 UTF8 編碼,所以在源檔案裡, "中文" 被儲存為 UTF8內碼.
然後在Windows中開啟這個源檔案,由於Windows使用GBK編碼,所以VC++ 按照GBK去解釋被儲存為 UTF8 內碼的 "中文", 顯示為亂碼.
2. wcout
在源檔案中定義 const wchar_t* str = L"中文" 在 VC++ 編譯器上,由於指定了L,所以字串 "中文" 被儲存為UNICODE內碼(UCS2),
編譯器也把 str 指向一個包含有 UNICODE 編碼的唯讀記憶體空間.
用 wcout 輸出 str 時, wcout 首先調用 wcstombs() (即根據當前 local 轉換, 如果沒有設定local,則是經典的C local, 不認識中文)把 str 的內容轉換後
交給控制台,結果自然什麼都不顯示. (調試代碼可以知道VC++ 2010 實現是一個字元一個字元輸出,調用 wctomb_s)
原理
我們知道 cout 和 wcout 分別是 basic_ostream 的特化版本, 而 basic_ostream 調用 basic_streambuf 實際執行輸出動作,針對 wchar_t,
basic_streambuf有專門的特化函數,調用 fputwc 輸出一個寬字元,而 fputwc 需要調用 wctomb_s 把寬字元轉換後再輸出. 我們知道wctomb_s 是依賴 locale 的
由於預設情況下是C locale,所以用中文內碼調用 wctomb_s 會失敗.
解決辦法
設定當前系統的locale 替代預設的 "C" locale, 使 wctomb_s 等函數可以正常工作.
以下3種方法中的任意一種都可以達到目的.
1. C函數設定全域locale
setlocale(LC_ALL, "");
2. C++ 設定全域locale
std::locale::global(std::locale(""));
2. 單獨為 wcout 設定一個 locale
std::locale loc("");
std::wcout.imbue(loc);
結論
和Windows API 不同 C++中的各種 w版本的類或者函數並不能提高效能,因為它們都需要用 wc..to..mb 之類的函數轉換為ANSI相容編碼然後調用標準庫函數.
或者,如果庫函數的實現者願意,針對Windows系統,寬字元的fputwc可以直接調用UNICODE版本的Windows API而不用轉換.但是這些都跟C++語言本身沒有什麼關係.
由於Windows核心是UNICODE的,所以直接用 UNICODE 字串調用 Windows API會有一點點好處.
C++設計者的出發點: 我不管你用什麼字元編碼,與C++無關,要輸出時:如果是單位元組字元或者多位元組字元,直接輸出;如果是寬字元,則根據local轉換為多位元組字元,然後再輸出.
即使將來UNICODE過時了(假設,假設而已),也不要緊,只要定義好新的local即可.對於C語言也是這樣.
Windows設計者的出發點: 統一使用 Unicode 寬字元,解決一切問題.