標籤:tle www. 注意 中文字元 中文 應該 tools 自己 源檔案
字元編碼的問題,上大學那會兒就遇到過,一直都是雲裡霧裡,沒太搞清楚。最近又遇到了問題,想在C++的控制台上輸出Utf-8編碼的漢字位元組流。嘗試了好多次都是亂碼,後來花了些時間查查資料,又和同事交流了一下,算是把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).分析一下控制台上顯示的結果。
1 char* str= “我”;2 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字元集裡找到對應編碼的漢字“鎴?”。這當然就錯了。
字元編碼可以到這個網站去查詢http://www.mytju.com/classcode/tools/encode_utf8.asp
解決方案
這就是我一開始出現的錯誤,既然知道問題了,那怎麼改呢,為了讓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(“chcp 65001”);這是命令列設定當前字碼頁的命令。
這樣應該就能正常顯示UTF8字元了,不過有個問題就是如果str用cout輸出的話,依然是亂碼,這個可能是因為cout有自己的解析字元集,不會隨著chcp命令改變。這個有待研究,哪位同學知道,可以留言告訴我。再說一點#pragma execution_character_set("utf-8")這個預先處理在C++11裡已經不再需要了,C++11可以指定字串字面量的執行字元集了,u8”我”。就這麼簡單。但是vs2013並不支援這個功能。這篇文章講述的內容,並不在於如何把一個UTF8格式的C++字面量輸出到控制台。而是在於通過這個例子來瞭解MSVC C++是如何處理UTF8中文字元的。
尊重他人智慧成果,若要轉載,請註明作者esfog,原文地址http://www.cnblogs.com/Esfog/p/MSVC_UTF8_CHARSET_HANDLE.html
MSVC中C++ UTF8中文編碼處理探究