MSVC中C++ UTF8中文編碼處理探究


  字符編碼的問題,上大學那會兒就遇到過,一直都是雲里霧里,沒太搞清楚。最近又遇到了問題,想在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的,就會用內存里的E6889122GBK字符集里找到對應編碼的漢字“鎴?”。這當然就錯了。

  字符編碼可以到這個網站去查詢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 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM