Qt字符编码认识
- 長期以來,很多人都清楚,一旦C++源碼中直接使用了中文,這樣的源碼想要跨平臺(I18N)會非常困難。
隨著:
- Windows下:MSVC2010成為主流
- Linux下:GCC升級到4.6
C++中的中文問題 才算有了一個比較優雅的、跨平臺的Workaround。
(本文討論編譯器范圍:GCC4.6+, MSVC2010sp1+ 。本文屬于QString系列,但暫不涉及QString)
C++ 中文問題
要在C++中正確使用中文,必須要了解下面兩個概念:
| 源碼字符集(the source character set) | 源碼文件是使用何種編碼保存的 |
| 執行字符集(the execution character set) | 可執行程序內保存的是何種編碼(程序執行時內存中字符串編碼) |
C++98的問題:?既沒有規定源碼字符集,也沒有規定執行字符集
這個... 如何理解?不妨看個例子
例子
這個要求高么?
- 一個簡單的C++程序,只是希望它能在簡體中文Windows、正體中文Windows、英文版Windows、Linux、MAC OS...下的結果一致。
//main.cpp int main() { char mystr[] = "老老實實的學問,來不得半點馬虎"; return sizeof mystr; }
可以試著反問自己兩個問題
- 這個源碼文件是何種編碼保存的?(有確定答案么?)
- mystr中是什么內容?(有確定答案么?)
對C++來說,這兩個都不確定。
- 固定平臺的話,還能忍忍
- 要跨平臺的話,這種東西...
GCC
在GCC下,這兩個都可以使用你自己喜好的編碼(如果不指定,默認都是UTF8)
-finput-charset=charset -fexec-charset=charset除了前兩個選項外,還有一個:
-fwide-exec-charset=charsetwide? 不妨先猜一下它是干嘛的
MSVC
MSVC沒有類似前面的選項。
| 源碼字符集如何解決? | 有BOM么,有則按BOM解釋,無則使用本地Locale字符集(隨系統設置而變) |
| 執行字符集如何解決? | 使用本地Locale字符集(隨系統設置而變) |
挺霸道哈(當然,源碼中可以使用#pragma?setlocale("..."),但功能很有限,比如Windows沒有utf8的locale,所以...)。
另外,和GCC對應的wide-exec-charset呢?
| 寬執行字符集如何解決? | 不妨先考慮一下 |
怎么辦?
這才兩個編譯器,看起來就這么復雜了。而C++編譯器的數目遠大于2.
要想跨平臺,必須確保這兩個字符集都是“確定”的,而能勝任該任務的字符集,似乎理想的也只能是...
UTF-8方案
-
如果我們將源碼保存成utf8,執行字符集也選為utf8,那么天下將太平了。使用非ASCII字符的源碼文件也就可以在不同國家的用戶間無障礙流通了?;-).
源碼保存成UTF-8沒有什么困難,但是,執行字符集需要是UTF-8。沒那么簡單
對GCC來說,這個問題很簡單(默認的編碼選項足夠了):
- 只要源碼文件保存成utf8即可(帶或不帶BOM均可)
- 早期的gcc不接收帶BOM的utf8源碼文件,現在,至少在GCC4.6中,這一限制不再存在。
對MSVC來說,這個問題異常復雜:
- 對MSVC2003來說,只要源碼保存成不帶BOM的utf8即可
- 對MSVC2005、(沒在SP1基礎上裝熱補丁的)MSVC2008來說。完全沒辦法
- 直到MSVC2010sp1,才算提供了一個解決方案。源碼保存成帶BOM的utf8,utf16,...,然后添加
#pragma execution_character_set("utf-8")
要想跨GCC4.6+和MSVC2010sp1+,我們需要取它們的交集:也就是
- 源碼保存成帶BOM的utf8
-
為MSVC添加#pragma
//main.cpp #if _MSC_VER >= 1600 #pragma execution_character_set("utf-8") #endif int main() { char mystr[] = "老老實實的學問,來不得半點馬虎"; return sizeof mystr; }
C++11
等到MSVC支持C++11的String Literals之時,我們就沒必要用那個蹩腳的pragma了,直接
char mystr[] = u8"老老實實的學問,來不得半點馬虎";即可(盡管現在在GCC下沒問題,但要跨平臺,估計要等到Visual C++ 12了)。
有個問題?
C++98中不是有個wchar_t么,它不是用來表示unicode字符的么?
Unicode 4.0標準的5.2節是如何說的:
The width of wchar_t is compiler-specific and can be as small as 8 bits. Consequently, programs that need to be portable across any C or C++ compilershould not use wchar_t for storing Unicode text. The wchar_t type is intended forstoring compiler-defined wide characters, which may be Unicode characters in some compilers.
在回頭看看GCC的選項
-fwide-exec-charset=charset盡管GCC為其提供的默認編碼是UTF16或UTF32(取決于wchar_t的寬度),但該編碼是可以隨意設置的。
盡管這個東西不保證跨平臺,也很不好玩,?但是,由于在windows下面wchar_t用來表示utf16字符,而且直接對應系統API接口,所以在類型char16_t普及之前,還是很重要的。
C++11執行字符集
前面提到的u8就是C++11為“執行字符集”所做的努力之一。
新明確規定了utf8、utf16和utf32這3種執行字符集。
| char* | u8"中文" |
| char16_t* | u"中文" |
| char32_t* | U"中文" |
可是C++11并沒有規定源碼字符集
const char* mystr=u8"中文";C++標準對編譯器說,我不管這個文件的具體編碼是什么,但你必須給我生成對應utf8編碼的字節流。
編譯器似乎有點傻了吧?不知道源文件的編碼,我如何轉換
于是:
MSVC說:源碼文件必須有BOM,不然我就認為你是本地locale的編碼
GCC說:我認為你就是utf8編碼,除非通過命令行通知我其他編碼
在C++11標準下,對源碼編碼?簡單的處理辦法還是,使用帶BOM的UTF8保存。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
- 上一篇: 邻近算法(KNN算法)
- 下一篇: node.js路由控制