C++輸出中文字符(轉)

C++輸出中文字符

 

1. couthtml


場景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語言也是這樣.ios

Windows設計者的出發點: 統一使用 Unicode 寬字符,解決一切問題函數

 

 

原文:http://blog.csdn.net/gonxi/article/details/5931006post

C/C++多字節字符與寬字符的輸出性能

使用C++標準庫的iostream,能夠方便地將控制檯、文件、字符串以及其它可擴充的外部表示做爲流來處理,但要處理中文,卻會碰到不少問題。本人原來沒怎麼用過這個iostream,這幾天嘗試用這個寫點東西,一下子不能輸出中文,一下子不支持中文文件名的,搞得頭大。網上搜了搜,沒有發現適用於全部狀況的解決方案。不事後來本身通過屢次測試,基本解決了這些問題,如今寫成文字做爲一個總結,也供碰到一樣問題的朋友參考。關於C語言中的 printf和wprintf的中文輸出,本文也進行了探討。

  須要說明的是,個人開發環境是VS 2005(標準庫固然也是微軟實現的),不保證其它環境下是相同的效果。
一、cout和wcout
  在缺省的C locale下,cout能夠直接輸出中文,但對於wcout卻不行。對於wcout,須要將其locale設爲本地語言才能輸出中文:
  wcout.imbue(locale(locale(),"",LC_CTYPE)); // ①
  也有人用以下語句的,但這會改變wcout的全部locale設置,好比數字「1234」會輸出爲「1,234」。
  wcout.imbue(locale(""));

二、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;

三、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)..測試

相關文章
相關標籤/搜索