C++輸出中文字符
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 首先調用 wcstomb_s() (即根據當前 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 寬字符,解決一切問題
原文:http://blog.csdn.net/gonxi/article/details/5931006
使用C++標准庫的iostream,可以方便地將控制台、文件、字符串以及其它可擴充的外部表示作為流來處理,但要處理中文,卻會碰到很多問題。本人原來沒怎么用過這個iostream,這幾天嘗試用這個寫點東西,一會兒不能輸出中文,一會兒不支持中文文件名的,搞得頭大。網上搜了搜,沒有發現適用於所有情況的解決方案。不過后來自己經過多次測試,基本解決了這些問題,現在寫成文字作為一個總結,也供碰到同樣問題的朋友參考。關於C語言中的 printf和wprintf的中文輸出,本文也進行了探討。
需要說明的是,我的開發環境是VS 2005(標准庫當然也是微軟實現的),不保證其它環境下是相同的效果。
1、cout和wcout
在缺省的C locale下,cout可以直接輸出中文,但對於wcout卻不行。對於wcout,需要將其locale設為本地語言才能輸出中文:
wcout.imbue(locale(locale(),"",LC_CTYPE)); // ①
也有人用如下語句的,但這會改變wcout的所有locale設置,比如數字“1234”會輸出為“1,234”。
wcout.imbue(locale(""));
2、ofstream和wofstream
在缺省的C locale下,ofstream能正確輸出中文到文件中,但不支持中文文件名;wofstream支持中文文件名,但不能向文件中輸出中文。要解決這個問題,需要在打開文件之前將全局locale設為本地語言。將全局locale設為本地語言后,ofstream和wofstream的問題都解決了,但 cout和wcout卻不能輸出中文了。要讓cout和wcout輸出中文,需要將全局locale恢復原來的設置,如下所示:
locale &loc=locale::global(locale(locale(),"",LC_CTYPE)); // ②
ofstream ofs("ofs測試.txt");
wofstream wofs(L"wofs測試.txt");
locale::global(loc); // ③
ofs<<"test測試"<<1234<<endl;
wofs<<L"Another test還是測試"<<1234<<endl;
3、printf和wprintf
加上這兩位C語言中的老兄,問題更加復雜。考慮如下語句(注意s的大小寫):
printf("%s", "multibyte中文/n"); // ④
printf("%S", L"unicode中文/n"); // ⑤
wprintf(L"%S", "multibyte中文/n"); // ⑥
wprintf(L"%s", L"unicode中文/n"); // ⑦
缺省情況下,⑤、⑦兩條語句不能輸出中文,這兩條語句中字符串的形式是unicode形式的。如果在所有輸出語句之前加上如下語句將C語言的全局locale設置為本地語言(C語言中只有全局locale)就可以正常輸出了:
setlocale(LC_CTYPE, ""); // ⑧
但這會導致cout和wcout不能輸出中文,將C語言的全局locale恢復后cout和wcout就正常了,如下所示:
setlocale(LC_CTYPE, "C"); // ⑨
但恢復后,printf和wprintf輸出Unicode文本又不正常了(輸出MultiByte文本總是正常的)。總不能每寫一個 printf/wprintf就設置一次然后再恢復一次吧?所以,建議不要混用iostream和printf/wprintf,實在要混用,那就讓 printf/wprintf只輸出MultiByte字符串,這樣不需要調用setlocale(),也就不會影響到cout和wcout。
總結
總之,用iostream、printf/wprintf輸出中文,有點麻煩。概括起來要點如下:
如果要用wcout,需要在使用之前按語句①將其locale設置為本地語言;
如果要用ofstream或wofstream,要在打開文件之前按語句②將全局locale設為本地語言並保存初始的全局locale。然后在打開文件之后,按語句③將全局locale恢復為初始值;
不要混用iostream和printf/wprintf。如果要混用,只用printf/wprintf輸出MultiByte字符串;
單獨使用printf/wprintf時,如果要輸出Unicode字符串,需要按語句⑧設置C語言的全局locale。如果只輸出MultiByte字符串,則不需設置。
最后再加上轉帖者(本站站長)的一點話:
一個程序,一般不會用兩種字符串, 要么用多字節字符串, 要么用寬字符串. 這樣,問題其實就很簡單, 沒作者說得那么復雜.. 就算有時候需要轉換, 也有專門的函數(例如,多字節字符版本的程序,使用COM組件, COM組件需要寬字符串. 則可以利用 _bstr_t, CString)..