3.7 Qt和字符集
3.7.1 計算機上的3種字符集
在計算機中每個字符都要使用一個編碼來表示,而每個字符究竟使用哪個編碼來表示要取決于使用哪個字符集(Charset)。
計算機字符集可歸類為3種:單字節字符集(SBCS)、多字節字符集(MBCS)和寬字符集(Unicode字符集)。
(1)單字節字符集(SBCS)
單字節字符集的所有字符都只有一個字節的長度。單字節字符集(SBCS)是一個理論指導規范。具體實現時有兩種字符集:ASCII字符集和擴展ASCII字符集。
ASCII字符集主要用于美國,是由美國國家標準局(ANSI)頒布的,全稱是美國國家標準信息交換碼(American National Standard Code For Information Interchange),使用7位(bit)來表示一個字符,總共可以表示128個字符(0~127),不過一個字節有8位,有1位沒有用到,因此人們把最高1位永遠設為0,用剩下的7位組成的編碼來表示字符集的128個字符。ASCII字符集包括英文字母、數字、標點符號等常用字符,如字符'A'的ASCII碼是65、字符'a'的ASCII碼是97、字符'0'的ASCII碼是48、字符'1'的ASCII碼是49。其他字符編碼的具體細節可以查看ASCII碼表。
在美國剛剛興起計算機的時候,ASCII字符集中的128個字符就夠用了,一切應用都是順順當當的。后來計算機發展到歐洲,歐洲各個國家的字符較多,128個就不夠用了,怎么辦?人們對ASCII碼進行了擴展,因此就有了擴展ASCII字符集。它使用8位來表示一個字符,即可表示256個字符,在前面0到127的編碼范圍內定義的字符與ASCII字符集中的字符相同,后面多出來的128個字符用來表示歐洲國家的一些字符,如拉丁字母、希臘字母等。有了擴展的ASCII字符集,計算機在歐洲的發展也就順風順水了。
(2)多字節字符集(MBCS)
隨著計算機普及到更多國家和地區(比如東亞和中東),需要的字符就更多了,8位的單字節字符集不能滿足信息這些國家和地區交流的需要。因此,為了能夠表示更多國家和地區的文字(比如中文),人們對ASCII碼繼續擴展,也就是在歐洲人擴展的基礎上再進行擴展,即英文字母和歐洲字符為了和擴展ASCII兼容,依然用1個字節表示字符,而對于更多國家和地區自己的字符(如中文字符)則用2個字節來表示,這就是多字節字符集(Multi-Byte Character System,MBCS),它也是一個理論指導規范,具體實現時各個國家或地區根據自己的語言字符分別實現了不同的字符集,比如中國大陸實現了GB-2312字符集(后來又擴展出GBK和GB18030)、中國臺灣地區實現了Big5字符集,日本實現了jis字符集。這些具體的字符集雖然不同,但實現的依據都是MBCS,也就是字符編碼256后面的字符都用2個字節來表示。
MBCS解決了歐美地區以外不同語言中字符的表示,但缺點也很明顯。MBCS在保留原有擴展ASCII碼(前面256個)的同時,用2個字節來表示其他語言中的字符,這樣會導致一個字節和兩個字節混在一起,使用起來不太方便。例如,字符串“你好abc”,字符數是5,而字節數是8(因為最后還有一個'\0')。對于用++或--運算符來遍歷字符串的程序員來說,這簡直就是噩夢。另外,各個國家或地區各自定義的字符集難免會有交集,比如使用簡體中文的軟件就不能在日文環境下運行(會顯示出亂碼)。
這么多國家或地區都定義了各自的多字節字符集,并以此來為自己的文字編碼,那么操作系統如何區分這些字符集呢?操作系統通過代碼頁(Code Page)來為各個字符集定義一個編號,比如437(美國英語)、936(簡體中文)、950(繁體中文)、932(日文)、949(朝鮮語_朝鮮)、1361(朝鮮語_韓國)等都是屬于代碼頁。在Windows操作系統的控制面板中可以設置當前系統所使用的字符集。例如,通過控制面板打開Windows 7的“區域和語言”對話框,然后切換到“管理”選項卡,可以看到當前非Unicode(也就是多字節字符集)程序使用的字符集,如圖3-6所示(在Windows 10中的設置界面與此類似)。
在圖3-6中選定的語言是“中文(簡體,中國)”,系統此時的代碼頁就是936。我們可以編寫一個控制臺程序驗證一下。注意,控制臺程序輸出窗口默認使用的代碼頁(字符集)就是操作系統的代碼頁,也可以調用函數SetConsoleOutputCP()修改控制臺窗口的代碼頁。這個函數雖是Windows API函數,但可以在Qt項目中使用。

圖3-6 區域和語言
(3)Unicode字符集
Unicode編碼被稱為統一碼、萬國碼或單一碼。為了把全世界所有的文字符號都統一進行編碼,標準化組織ISO提出了Unicode編碼方案。這個編碼方案可以容納世界上所有文字和符號的字符編碼,并規定任何語言中的任一字符都只對應一個唯一的數字。這個數字被稱為代碼點(Code Point),或稱為碼點、碼位,用十六進制書寫,并加上U+前綴,比如'田'的代碼點是U+7530、'A'的代碼點是U+0041。
所有字符及其Unicode編碼構成的集合叫Unicode字符集(Unicode Character Set,UCS)。早期的版本有UCS-2,用兩個字節進行編碼,最多能表示65535個字符。在這個版本中,每個代碼點的長度有16位(比特位),用0至65535之間的數字來表示世界上的字符(當初以為夠用了),其中0至127這128個數字表示的字符依舊與ASCII碼中的字符完全一樣,比如Unicode和ASCII中的數字65都表示字母 'A'、數字97都表示字母 ' a'。反過來卻是不同的,字符 'A'在Unicode中的編碼是0x0041、在ASCII中的編碼是0x41,雖然它們的值都是97,但是編碼的長度是不一樣的(Unicode碼是16位長度,ASCII碼是8位長度)。
UCS-2后來不夠用了,又推出UCS-4版本。UCS-4用4個字節編碼(實際上只用了31位,最高位必須為0),它根據最高字節分成27=128個組(最高字節的最高位恒為0,所以有128個組)。每個組再根據次高字節分為256個平面(Plane)。每個平面根據第3個字節分為256行(Row),每行有256個碼位(Cell)。組0的平面0被稱作基本多語言平面(Basic Multilingual Plane,BMP),即范圍在U+00000000到U+0000FFFF的代碼點,若將UCS-4 BMP前面的兩個零字節去掉則可得到UCS-2(U+0000 ~ U+FFFF)。每個平面有216=65536個碼位。Unicode計劃使用了17個平面,一共有17×65536=1114112個碼位。在Unicode 5.0.0版本中,已定義的碼位只有238605個,分布在平面0、平面1、平面2、平面14、平面15、平面16。其中,平面15和平面16上只是定義了兩個各占65534個碼位的專用區(Private Use Area),分別是0xF0000~0xFFFFD和0x100000~0x10FFFD。所謂專用區,就是保留給大家放自定義字符的區域,可以簡寫為PUA。平面0也有一個專用區:0xE000~0xF8FF,有6400個碼位。平面0的0xD800~0xDFFF共有2048個碼位,是一個被稱作代理區(Surrogate)的特殊區域。代理區的目的是用兩個UTF-16字符表示BMP以外的字符(在講UTF-16編碼時會介紹)。
在Unicode 5.0.0版本中,238605-65534×2-6400-2408=99089,余下的99089個已定義碼位分布在平面0、平面1、平面2和平面14上,對應Unicode目前定義的99089個字符,其中包括71226個漢字。平面0、平面1、平面2和平面14上分別定義了52080、3419、43253和337個字符。平面2的43253個字符都是漢字。平面0上定義了27973個漢字。
再歸納總結一下:
① 在Unicode字符集中的某個字符對應的代碼值稱作代碼點(Code Point),簡稱碼點,用十六進制書寫,并加上U+前綴。
② 后來字符越來越多,最初定義的16位(UCS-2版本)已經不夠用,就用32位(UCS-4版本)表示某個字符的代碼點,并且把所有代碼點分成17個代碼平面(Code Plane):其中,U+0000~U+FFFF劃入基本多語言平面(Basic Multilingual Plane,BMP);其余劃入16個輔助平面(Supplementary Plane),代碼點范圍為U+10000~U+10FFFF。
③ 并不是每個平面中的代碼點都對應有字符,有些是保留的,有些是有特殊用途的。
3.7.2 VC-Qt開發環境對Unicode和多字節的支持
VC下Qt開發環境支持兩種字符集:多字節字符集和Unicode字符集,默認采用Unicode字符集。我們可以創建項目驗證一下。
【例3.6】 VC下驗證默認情況的Qt項目字符集
(1)啟動VC2017,新建一個Qt控制臺項目,項目名為test。
(2)打開項目屬性對話框,展開左邊的“C/C++→預處理器”,在右邊“預處理器定義”旁可以看到UNICODE和_UNICODE的宏定義,如圖3-7所示。

圖3-7
這就說明Qt項目默認采用的是Unicode字符集。如果喜歡多字節字符集,可以把UNICODE和_UNICODE刪掉,參看下例。
【例3.7】 在VC下使用多字節字符集
(1)啟動VC2017,新建一個Qt控制臺項目,項目名為test。
(2)打開項目屬性對話框,展開左邊的“C/C++→預處理器”,在右邊“預處理器定義”旁可以看到UNICODE和_UNICODE的宏定義,把UNICODE和_UNICODE刪掉,然后單擊“確定”按鈕關閉項目屬性對話框。
(3)打開test.cpp,輸入如下代碼:

因為我們刪除了UNICODE和_UNICODE兩個宏定義,所以這里的TCHAR相當于char,_tcslen相當于strlen,因此運行結果都是5。
(4)保存項目并運行,結果如圖3-8所示。

圖3-8
新建一個VC項目后,可以在項目屬性里選擇本項目所使用的字符集(左邊選擇“常規”,右邊就能看到“字符集”選項了)。一定要記住:此選項只控制TCHAR、_T和通用形式的Win32 API函數是用寬字符版的還是多字節字符版的。如果選擇了“使用Unicode字符集”,那么代碼里用到的API函數就會被解釋為UNICODE版本的API(帶標記W的API),比如MessageBox被解釋為MessageBoxW。如果選擇了“使用多字節字符集”,則代碼里用到的API函數被解釋為多字節版本的API(帶標記A的API),如MessageBox被解釋為MessageBoxA。再比如對于代碼中的宏_T,如果選擇了Unicode字符集,則被解釋成L,其后的字符串是雙字節字符串;如果選擇多字節字符集,則其后的字符串是單字節字符串。
如果項目中使用了“多字節字符集”(就是系統預定義了宏_MBCS),則類型TCHAR將映射到char。如果項目中使用了“Unicode字符集”(就是系統預定義了宏_UNICODE),則類型TCHAR將映射到wchar_t。在上例中,如果我們選擇字符集選項為“Unicode字符集”,那么輸出就會是5和4了。我們可以驗證一下。
【例3.8】 通過字符集選項設置Unicode字符集
(1)打開VC2017,新建一個Qt控制臺項目,項目名為test。
(2)打開項目屬性對話框,展開左邊窗格中的“C/C++→預處理器”,在右邊窗格的“預處理器定義”旁可以看到UNICODE和_UNICODE的宏定義,把UNICODE和_UNICODE刪掉,然后單擊“確定”按鈕關閉項目屬性對話框。
(3)打開test.cpp,輸入如下代碼:

因為我們刪除了UNICODE和_UNICODE兩個宏定義,所以這里的TCHAR相當于char,_tcslen相當于strlen,應該都是5。
現在通過選中字符集的方式改回Unicode。打開項目屬性對話框,在左邊的窗格中選擇“配置屬性→常規”,在右邊窗格的“字符集”旁邊選擇“使用Unicode字符集”,如圖3-9所示。然后單擊“確定”按鈕,項目的字符集就切換到Unicode了,所以這里的TCHAR相當于wchar_t,_tcslen相當于wcslen,也就是5和4。

圖3-9
(4)保存項目并運行,運行結果如圖3-10所示。

圖3-10
預處理中的UNICODE、_UNICODE與“常規”中的“字符集”選項,哪個優先級高?答案是前者高。如果不刪除預處理中的UNICODE和_UNICODE,而把“常規”中的“字符集”選擇為“使用多字節字符集”,那么最終運行結果依然是當成Unicode編碼進行處理。
以上就是在VC下控制項目字符集的方式。在Qt中,建議采用支持Unicode字符集的開發軟件,因為使用Unicode字符集開發熱鍵好處頗多,比如:
· Unicode字符集使程序的國際化變得更容易。
· Unicode字符集提升了應用程序的效率,因為代碼執行速度更快,占用內存更少。Windows內部的一切工作都是使用Unicode編碼的字符和字符串來進行的。所以,假如你非要傳入ANSI編碼的字符或字符串,Windows就會被迫分配內存,并將ANSI字符或字符串轉換為等價的Unicode編碼的形式。
· 使用Unicode字符集,應用程序能輕松調用所有的Windows函數,因為一些Windows函數提供了處理Unicode字符和字符串的版本。
· 使用Unicode字符集,代碼很容易與COM集成(后者要求使用Unicode編碼的字符和字符串)。
· 使用Unicode字符集,代碼很容易與.NET Framework集成(后者要求使用Unicode編碼的字符和字符串)。
3.7.3 Qt Creator開發環境對Unicode和多字節的支持
在3.7.2小節我們驗證了VC-Qt對Unicode和多字節兩種字符集的支持情況,那么Qt Creator對Unicode和多字節又是如何支持或如何進行切換的呢?
下面幾個例子將使用Qt Creator 4.8.2,但要注意兩點:一是項目路徑中不要有中文,二是項目路徑要預先在磁盤上創建好。這也是筆者建議大家使用VC的原因之一,大公司的產品就是功能完善。
【例3.9】 在Qt Creator中驗證默認情況下Qt項目的字符集
(1)啟動Qt Creator 4.8.2,新建一個Qt控制臺項目,項目名為test。
(2)在test.cpp中輸入如下代碼:

Qt Creator創建的項目默認采用的字符集是Unicode,所以上面的代碼將輸出aaabbb。
(3)保存項目并運行(按Ctrl+R快捷鍵),結果如圖3-11所示。

圖3-11
要在Qt Creator項目中使用多字節字符集,該怎么辦呢?方法是在項目文件(.pro文件)中進行手工設置。請看下例。
【例3.10】 在Qt Creator中使用多字節字符集
(1)啟動Qt Creator 4.8.2,新建一個Qt控制臺項目,項目名為test,注意路徑中不要含中文或空格。
(2)在test.cpp中輸入如下代碼:

(3)在Qt Creator的項目視圖中,雙擊test.pro項目文件以打開它,在QT-=gui語句下面添加一行語句DEFINES-=UNICODE,即:
QT -= gui DEFINES -= UNICODE
(4)保存項目并運行,運行結果如圖3-12所示。

圖3-12
通過這兩個小例子就可以知道在Qt Creator中創建項目時默認采用的字符集是什么以及如何修改默認字符集了。
3.7.4 在Qt Creator中使用中文字符的兩種方式
為了在Qt程序中正確顯示中文而不出現亂碼,通常有兩種解決方案。
(1)使用靜態函數QString::fromLocal8Bit
該函數將字節數組對象轉為QString類對象,這個函數的原型聲明如下:
QString QString::fromLocal8Bit(const QByteArray &str)
在要顯示中文的地方這樣調用:
QString::fromLocal8Bit ("信息1")
為了代碼簡潔,也可以把QString::fromLocal8Bit定義為一個宏:
#define z QString::fromLocal8Bit
之后,在要顯示中文的地方這樣調用:
z("信息2")
(2)在文件開頭使用預處理命令execution_character_set
#pragma execution_character_set("utf-8") //用于正確顯示中文
這樣,在后面的代碼里就可以直接使用中文了。
- Maven Build Customization
- Ceph Cookbook
- 劍指Offer(專項突破版):數據結構與算法名企面試題精講
- Python測試開發入門與實踐
- Hands-On Enterprise Automation with Python.
- Getting Started with NativeScript
- 從Excel到Python:用Python輕松處理Excel數據(第2版)
- Django實戰:Python Web典型模塊與項目開發
- 深入實踐Kotlin元編程
- ASP.NET求職寶典
- Python應用開發技術
- Design Patterns and Best Practices in Java
- 輕松學Scratch 3.0 少兒編程(全彩)
- Eclipse開發(學習筆記)
- Learning Redis