1. 程式人生 > 實用技巧 >【轉載】MSVC中C++ UTF8中文編碼處理探究

【轉載】MSVC中C++ UTF8中文編碼處理探究

字符集


  先說一個名詞:字符集,沒聽過的先百度一下,其實就是一種將字元編碼的格式,像我們常說的ASCII,UTF8,GBK都是常用的字符集。

  首先要清楚,從你在編輯器裡輸入一個UTF8漢字開始,到最終在控制檯上顯示出來,整個流程涉及到三個概念,分別是原始碼字符集,執行字符集,解析字符集。

  分別解釋一下:

  原始碼字符集:就是你的原始碼文字檔案的字符集,如果你手頭有NodePad++這樣類似的文字編輯器你可以開啟看一下你的字符集,或者用Windows記事本另存為的時候也會顯示文字格式。要知道,你的原始碼文字檔案是以二進位制的形式躺在硬盤裡的,無論中文英文都一樣,當你輸入一個漢字後儲存關閉,這個漢字就是按照你指定的字符集轉換成二進位制編碼儲存下去的,當你在以這個格式開啟檔案時候,就再按照你指定的字符集把二進位制轉回來。如果兩次使用不同的字符集,也就會出現亂碼了。

  執行字符集:在C++裡char* str=“我”;執行字符集決定了這行程式碼在編譯器進行編譯的時候str儲存的位元組到底是什麼,你可能會說原始碼字符集不是已經決定了這個”我”的二進位制表示了麼,沒錯,但是這個執行字符集就是讓你在這裡對它再解釋一次。比如我原始碼字符集可能是UTF8的,但是我可以通過執行字符集來讓最終ptr儲存的是GBK的位元組編碼。

  解析字符集:最終要還原顯示這些二進位制位元組編碼的時候,就需要用到它。比如通過printf把前面的str顯示到控制檯時,這個printf就會按照解析字符集來解析這些位元組編碼,找到指定字元顯示出來。

  饒了一圈,好像也不是很亂,但是這裡面是有很多坑的。這幾個字符集的處理都是跟具體編譯器甚至作業系統相關的,不同的編譯器是有差別的,我這裡只說Windows7系統下VS2013(msvc編譯器)的環境。

VS2013中的字符集概念


1.對於原始碼字符集在VS2013檔案->高階儲存選項->編碼中可以檢視設定當前原始碼檔案的原始碼字符集。

2.對於執行字符集,VS2013預設根據系統的Locale來決定執行字符集,一般大家都是windows中文系統,Locale是中國,那麼就是GBK編碼。

3.對於解析字符集,我試了一下,如果沒有手動更改的話VS2013的標準輸入輸出(printf)到命令列也是根據系統Locael決定的,也就是GBK。

案例分析


  現在我們就分析一下,假如下面這段原始碼我們用UTF8格式儲存(無Bom).分析一下控制檯上顯示的結果。

char* str= “我”;
printf(“%s\n”,str);

1.首先這個程式碼檔案的文字中”我”這個漢字是以E68891三個位元組編碼的.

2.當編譯器編譯這段程式碼時,執行字符集預設是GBK,那麼編譯器要決定str的位元組內容,就要把文本里儲存的位元組內容轉為GBK,這裡就有個值得注意的問題,既然要轉換到GBK,就需要知道從什麼格式轉換到GBK,MSVC怎麼知道源格式呢?方法只有一個就是分析你的原始檔有沒有有BOM,要是有就按照BOM它就認為原格式就是BOM指定的格式(不瞭解BOM可以先百度一下),如果沒有BOM他就認為你的原始碼字符集是Locale關聯的。剛才說了我們是用UTF8無BOM格式儲存的原始檔,所以編譯器認為原始碼文字中的”我”是GBK編碼儲存的。

3.那從GBK到GBK,MSVC不會進行任何轉換,這裡有個小問題,提醒一下,這個程式碼應該是編譯不通過的,因為GBK中漢字是2個位元組表示的,而UTF8中是三個位元組,所以編譯器為了湊數會把”我”字後面的雙引號給吃掉,轉成了兩個GBK漢字編碼E688,9122(22是引號的UTF8編碼),沒有引號編譯器就會報錯,最簡單的解決辦法就是在在後面在加一個漢字變成偶數個就沒問題了。

4.程式執行起來後printf輸出到控制檯,這時候用到的解析字符集也是GBK的,就會用記憶體裡的E688,9122去GBK字符集裡找到對應編碼的漢字“鎴?”。這當然就錯了。

解決方案


這就是我一開始出現的錯誤,既然知道問題了,那怎麼改呢,為了讓UTF8編碼的原始檔中的”我”字可以顯示到命令列上,我們需要進行如下分析:

1.首先一定要在編譯的時候讓str的位元組內容是UTF8格式的才行,那就需要讓執行字符集是UTF8才行,前面說到MSVC執行字符集是根據Locale來決定的,本來是沒法更改的,但是微軟後來打了個小不定添加了一個預處理#pragma execution_character_set("utf-8")。來告訴編譯器執行字符集設定為UTF8。

2.編譯時候進行轉換到執行字符集需要知道原始碼字符集,之前我們是沒有帶BOM,這導致MSVC認為我們的原始檔是GBK編碼的,但其實我們是UTF8編碼,這就需要我們儲存原始碼的時候改為用UTF8帶BOM的格式。這樣就不會有問題了。

3.最後要顯示出來,既然記憶體裡是UTF8編碼,解析肯定也要按UTF8格式來解析,所以我們要把預設的解析字符集從GBK設為UTF8,最簡單的方法就是在輸出之前呼叫system(“chcp65001”);這是命令列設定當前內碼表的命令。

  這樣應該就能正常顯示UTF8字元了,不過有個問題就是如果str用cout輸出的話,依然是亂碼,這個可能是因為cout有自己的解析字符集,不會隨著chcp命令改變。這個有待研究,哪位同學知道,可以留言告訴我。再說一點#pragma execution_character_set("utf-8")這個預處理在C++11裡已經不再需要了,C++11可以指定字串字面量的執行字元集了,u8”我”。就這麼簡單。但是vs2013並不支援這個功能。這篇文章講述的內容,並不在於如何把一個UTF8格式的C++字面量輸出到控制檯。而是在於通過這個例子來了解MSVCC++是如何處理UTF8中文字元的。

原文地址http://www.cnblogs.com/Esfog/p/MSVC_UTF8_CHARSET_HANDLE.html