C++中的中文編碼 亂碼的根源及解決方案

總結

ASCII規定了127個字符編碼,而一個字節最多能表示256種。所以可以根據第一位來判斷是不是ASCII編碼,如果不是說明這是一個多字節編碼。

char 與 std::string,英文字符在 UTF-8 中使用一個字節存儲,中文字符使用三個字節存儲。

C++ 11 開始支持 UTF-8、UTF-16 和 UTF-32 字符串常量的聲明,分別使用 u8""、u"" 和 U"" 作爲聲明的標誌。

wchar_t 與 std::wstring解決多字符編碼,佔兩個字節,且需要使用適配的 std::wcout 和 std::wofstream。windows中的編譯器一般將wchar_t定爲2個字節寬,而linux中的編譯器一般定義wchar_t爲4字節寬。用常量字符給wchar_t變量賦值時,前面要加L。如: wchar_t wch2 = L’中’;用常量字符串給wchar_t數組賦值時,前面要加L。如: wchar_t wstr2[3] = L”中國”。

編碼場景:

  • 源碼字符集(the source character set):源代碼文件是使用何種編碼字符集保存的
  • 執行字符集(the execution character set):源代碼經過編譯、鏈接後的可執行文件是使用何種編碼字符集保存的,程序實際執行時,內存中的字符串編碼就是執行字符集
  • 運行環境編碼:操作系統(或者當前控制檯環境)用於顯示文字的編碼字符集

亂碼的根源:源代碼文件(源碼字符集)經過編譯/鏈接,生成可執行文件(執行字符集),最後程序運行於實際環境中(運行環境編碼)。在這過程中如果有字符集不匹配,最終就無法顯示預期的文字信息,甚至產生亂碼。

  • 編譯器在編譯源代碼時,會將源碼字符集轉化爲執行字符集,如果編譯器不能正確識別源碼字符集,就得不到正確的字符串數據。
  • 可執行文件在實際運行環境中執行時,爲了在控制檯(或者其他UI)上顯示出字符串,就要將執行字符集轉化爲運行環境的字符集。如果運行環境的字符集與執行字符集不同,也會導致亂碼。

總結起來,要想使程序不會亂碼,必須滿足:

  • 編譯器準確識別了源碼字符集,從而得到正確的字符串數據(執行字符集)。

  • 運行環境的編碼與執行字符集相同。

代碼相關

一般在程序中爲了支持國際化,在程序初始化時,將 locale 設置爲系統配置的 Native ANSI 字符集,即執行:setlocale(LC_ALL, “”)。

可以查看文件編碼格式:

:file main.cc
main.cc: C++ source text, UTF-8 Unicode text

GCC的源碼字符集與執行字符集默認都是UTF-8編碼,也就是說默認情況下GCC都是按UTF-8來解析源碼,編譯後的執行字符集也是UTF-8。當然GCC也提供改變默認情況的編譯選項:

-finput-charset=charset 用於指定源碼字符集
-fexec-charset=charset 用於指定執行字符集

還有一個參數是:

-fwide-exec-charset=charset

默認情況下,gcc在Windows平臺下,寬字符串串常量的每個字符是16位UTF-16類型,在Linux平臺下,寬字符串串常量的每個字符是32位UTF-32類型,使用-fwide-exec-charset=charset這個參數,可以改變寬字符串串常量的類型。例如在x86的機器環境,Linux操作系統下,要使例如 L"漢字" 編譯後保存爲UTF-16的字符串,則可以使用 -fwide-exec-charset=UTF-16LE。

控制檯查看編碼:

:locale
LANG="zh_CN.UTF-8"
LC_COLLATE="zh_CN.UTF-8"
LC_CTYPE="zh_CN.UTF-8"
LC_MESSAGES="zh_CN.UTF-8"
LC_MONETARY="zh_CN.UTF-8"
LC_NUMERIC="zh_CN.UTF-8"
LC_TIME="zh_CN.UTF-8"
LC_ALL=
參考

https://sf-zhou.github.io/programming/chinese_encoding.html

https://www.iteye.com/blog/jimmee-2165685

https://blog.csdn.net/benkaoya/article/details/59522148

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章