GB18030有兩個版本:GB18030-2000和GB18030-2005。在本文中,沒有指明版本的GB18030是指GB18030-2005。本文討論了以下問題:
1. GB2312有682個圖形符號,都放在1區(qū)。GBK的1區(qū)有717個圖形符號,5區(qū)有 166個圖形符號,一共有883個圖形符號。GB18030的1區(qū)有728個圖形符號,5區(qū)還是166個符號。那么,GBK的1區(qū)在GB2312基礎(chǔ)上增 加了哪35個符號?GB18030又增加了哪些符號?
2. GBK支持21003個漢字與883個圖形符號,一共21886個字符。這21886個字符究竟是哪些字符?這21886個字符的編碼在GB18030中有什么變化?
3. GB18030是怎樣映射Unicode的全部0x110000個碼位的?
4. GB18030-2000和GB18030-2005在字匯上有什么區(qū)別,在編碼上有什么區(qū)別?
5. GB18030-2005的雙字節(jié)區(qū)中有2067個碼位被映射到Unicode BMP的PUA。這些碼位有什么規(guī)律?這些碼位中定義了多少字符?其實(shí)這2067個碼位中只定義了24個字符。
6. GBK的21886個字符中有95個字符被映射到Unicode BMP的PUA。在GB18030中這95個字符的編碼有哪些變化?哪些字符保持了原來的編碼?
7. GBK的23940個碼位中有多少碼位被映射到Unicode BMP的PUA?在GB18030中這些碼位的編碼有什么變化?
在討論這些問題前,我們先約定一下碼位空間的表示方法。
0 碼位空間
0.1 約定
GBK是雙字節(jié)編碼,每個字符用兩個字節(jié)表示。GB18030是多字節(jié)字符集,它的字符可以用一個、兩個或四個字節(jié)表示。碼位空間由各字節(jié)的范圍確定。例如:GB18030的四字節(jié)字符碼位空間是:
· 第一字節(jié)在0x81~0xFE之間
· 第二字節(jié)在0x30~0x39之間
· 第三字節(jié)在0x81~0xFE之間
· 第四字節(jié)在0x30~0x39之間
為了表述方便,我們用0x81308130~0xFE39FE39表示這個碼位空間。也就是說:在本文中0x81308130~0xFE39FE39所指的并不是從0x81308130到0xFE39FE39的連續(xù)2097773834(0xFE39FE39-0x81308130+1)個字節(jié)。在本文中,0x81308130~0xFE39FE39所指的是編碼的各字節(jié)在對應(yīng)范圍內(nèi)的碼位空間,這個碼位空間的碼位數(shù)目是:
(0xFE-0x81+1)*(0x39-0x30+1)*(0xFE-0x81+1)*(0x39-0x30+1)=126*10*126*10=1587600
同理,0xB0A1~0xF7FE代表的碼位空間是第一字節(jié)在0xB0~0xF7之間,第二字節(jié)在0xA1~0xFE之間的所有碼位。這個碼位空間的碼位數(shù)目是:
(0xF7-0xB0+1)*(0xFE-0xA1+1)=72*94=6768
這個碼位空間就是GBK和GB18030的2區(qū),在這6768個碼位中定義了6763個字符。
本文用~表示上述碼位空間,用-表示一般的范圍,即:
· 0xA1A1~0xA9FE 表示第一字節(jié)在0xA1到0xA9之間,第二字節(jié)在0xA1~0xFE之間的846((0xA9-0xA1+1)*(0xFE-0xA1+1)=9*94)個碼位。
· 0xE000-0xF8FF 表示從0xE000-0xF8FF的連續(xù)6400(0xF8FF-0xE000+1)個碼位。
0.2 習(xí)題
讀者如果已經(jīng)理解了上面的約定,請完成下面兩個習(xí)題:
1. 習(xí)題一:求碼位空間0x8140~0xFE7E的碼位數(shù)目。
2. 習(xí)題二:求碼位空間0x8180~0xFEFE的碼位數(shù)目。
0.3 答案
以下是習(xí)題0.2的答案:
1. 習(xí)題一:(0xFE-0x81+1)*(0x7E-0x40+1)=126*63=7938
2. 習(xí)題二:(0xFE-0x81+1)*(0xFE-0x80+1)=126*127=16002
GB18030雙字節(jié)字符的碼位空間就是0x8140~0xFE7E和0x8180~0xFEFE,雙字節(jié)字符的碼 位數(shù)目是7938+16002=23940。0x8140~0xFE7E和0x8180~0xFEFE也是GBK的全部碼位空間。GBK在這23940個 碼位中定義了21886個字符。
1 GBK回顧
1.1 簡介
GBK是雙字節(jié)編碼方案。它的碼位空間就是前面所說的0x8140~0xFE7E和0x8180~0xFEFE,一共23940個碼位。在這23940個碼位上定義了21886個字符,包括21003個漢字和883個圖形符號。《Unicode、GB2312、GBK和GB18030中的漢字》詳細(xì)討論了這21003個漢字。本文的第3節(jié)會討論GB2312、GBK和GB18030的圖形符號。
GBK的碼位空間可以劃分為以下區(qū)域:
類別 | 區(qū)名 | 碼位范圍 | 碼位數(shù) | 字符數(shù) |
符號區(qū) | 1區(qū) | 0xA1A1~0xA9FE | 846 | 717 |
5區(qū) | 0xA840~0xA97E和0xA880~0xA9A0 | 192 | 166 |
漢字區(qū) | 2區(qū) | 0xB0A1~0xF7FE | 6768 | 6763 |
3區(qū) | 0x8140~0xA07E和0x8180~0xA0FE | 6080 | 6080 |
4區(qū) | 0xAA40~0xFE7E和0xAA80~0xFEA0 | 8160 | 8160 |
用戶自定義區(qū) | 用戶區(qū)1 | 0xAAA1~0xAFFE | 564 | |
用戶區(qū)2 | 0xF8A1~0xFEFE | 658 | |
用戶區(qū)3 | 0xA140~0xA77E和0xA180~0xA7A0 | 672 | |
1.2 GBK字符與Unicode的映射
我制作了一個Excel文件:附件1。這個文件包含3張表格:
1. 按照GBK編碼排序的GBK全部21886字符碼表。這個表格有3列:字符、GBK編碼、Unicode編碼。
2. 按照Unicode編碼排序的GBK全部21886字符碼表。這個表格有3列:字符、Unicode編碼、GBK編碼。
3. 從 按Unicode編碼排序的表格中,很容易找到被映射到PUA(0xE000-0xF8FF)的字符。GBK的21886個字符中有95個字符屬于 PUA。第三張表格列出了這95個字符(A列)的GBK編碼(B列)、Unicode編碼(C列)以及這些字符在GB18030中對應(yīng)的Unicode編 碼(D列)。
其中D列可能不太容易理解,我再解釋一下。GB18030是兼容GBK的,所以這些字符的GBK編碼和GB18030編碼是相同的。例如的GBK編碼和GB18030編碼都是0xA8BF。但是在GBK和GB18030中,被映射到不同的Unicode碼位。在GBK中, 0xA8BF被映射到Unicode的0xE7C8。在Unicode中,碼位0xE7C8是一個PUA碼位,保留給用戶使用。在GB18030中, 0xA8BF被映射到Unicode的0x01F9。在Unicode中,碼位0x01F9屬于“拉丁字母擴(kuò)充-B”這個Block,這個碼位定義的字符 是“帶抑音符的拉丁文小寫字母 N”,字形就是。
1.3 GBK碼位與Unicode的映射
GBK的23940個碼位定義了21886個字符,還有23940-21886=2054個空閑碼位,這2054個 碼位都被映射到Unicode的PUA。在設(shè)計(jì)GBK時,GBK的21886個字符中有95個在Unicode中沒有對應(yīng)字符,所以這95個字符也被映射 到Unicode的PUA。在GBK的23940個碼位中,一共有2054+95=2149個碼位被映射到PUA,對應(yīng)的PUA編碼是0xE000- 0xE864。0xE000-0xE864就是2149個碼位。這2149個碼位的分配有以下規(guī)律:
碼位所在區(qū)域 | 碼位數(shù)量 | 映射到的PUA范圍 |
用戶區(qū)1:0xAAA1~0xAFFE | 564 | 0xE000-0xE233 |
用戶區(qū)2:0xF8A1~0xFEFE | 658 | 0xE234-0xE4C5 |
用戶區(qū)3:0xA140~0xA77E和A180-A7A0 | 672 | 0xE4C6-0xE765 |
符號區(qū)(1區(qū)和5區(qū))的170個空閑碼位 | 170 | 0xE766-0xE80F |
2區(qū)的5個空閑碼位:0xD7FA-0xD7FE | 5 | 0xE810-0xE814 |
4區(qū)的80個Unicode當(dāng)時沒有定義的字符:FE50-FE7E和FE80-FEA0 | 80 | 0xE815-0xE864 |
附件2包含兩張表格:
1. 23940個GBK碼位與Unicode的映射。兩組數(shù)據(jù)分別按GBK和Unicode排序。
2. 2149個映射到PUA的碼位,按Unicode順序排列。
2 GB18030編碼
2.1 概述
GB18030是多字節(jié)字符集,它的字符可以用一個、兩個或四個字節(jié)表示。GB18030的碼位定義如下:
字節(jié)數(shù) | 碼位空間 | 碼位數(shù) | 字符數(shù) |
單字節(jié) | 0x00~0x7F | 128 | 128 |
雙字節(jié) | 0x8140~0xFE7E和0x8180~0xFEFE | 23940 | 21897 |
四字節(jié) | 0x81308130~0xFE39FE39 | 1587600 | 54531 |
GB18030有128+23940+1587600=1611668個碼位。Unicode的碼位數(shù)目是0x110000(1114112),少于GB18030。所以,GB18030有足夠的空間映射Unicode的所有碼位。
GB18030的1611668個碼位目前定義了128+21897+54531=76556個字符。Unicode 5.0定義了99089個字符。
2.2 設(shè)計(jì)思路
GB18030編碼可以分為:單字節(jié)部分、雙字節(jié)部分和四字節(jié)部分。單字節(jié)部分與Unicode的0x00-0x7f完全相同。雙字節(jié)部分與GBK有兩點(diǎn)差異:
1. 在1區(qū)增加了11個字符。這樣1區(qū)就有717+11=728個字符。增加的11個字符是:一個歐元符號(0xA2E3)和10個豎排標(biāo)點(diǎn)符號(0xA6D9-0xA6DF、0xA6EC-0xA6ED和0xA6F3)。
2. 原來因?yàn)?span lang="EN-US">Unicode沒有收錄而映射到PUA的字符中的部分字符被新版本的Unicode收錄,所以將這些字符映射到非PUA的碼位。
Unicode的BMP一共有65536個碼位。其中代理區(qū)(0xD800-0xDFFF)有2048個碼位,這 2048個碼位是不能定義字符的。GB18030的單字節(jié)部分映射了128個碼位,GB18030的雙字節(jié)部分映射了23940個碼位。還剩下65536 -2048-128-23940=39420個碼位。
GB18030將這39420個碼位順序映射到從0x81308130開始的碼位空間。GB18030將 Unicode的16個輔助平面(0x10000-0x10FFFF,一共1048576個碼位)順序映射到從0x90308130開始的碼位空間。 GB18030四字節(jié)部分中只有這兩個區(qū)域定義了字符,其它空間都是保留區(qū)和自定義區(qū)。本文的第3節(jié)和第4節(jié)還會詳細(xì)討論GB18030的雙字節(jié)和四字節(jié)部分。
GB18030的設(shè)計(jì)思路可以概括到以下幾點(diǎn):
1. 單字節(jié)部分與Unicode一致。
2. 雙字節(jié)部分與GBK兼容。適當(dāng)調(diào)整一些字符與Unicode的映射。這些字符原來因?yàn)?span lang="EN-US">Unicode沒有收錄而被映射到PUA,現(xiàn)在因?yàn)?span lang="EN-US">Unicode已經(jīng)收錄而調(diào)整到非PUA的Unicode碼位。
3. 將Unicode BMP部分還沒有映射的39420個碼位順序映射到從0x81308130開始的四字節(jié)部分。
4. 將Unicode BMP以外的16個輔助平面映射到39420個碼位順序映射到從0x90308130開始的四字節(jié)部分。
在GB18030目前定義的76556個字符中,只有24個字符被定義到Unicode的PUA區(qū)。這24個字符包 括1區(qū)的10個豎排標(biāo)點(diǎn)符號(0xA6D9-0xA6DF、0xA6EC-0xA6ED和0xA6F3)和4區(qū)的14個漢字(0xFE51、 0xFE52、0xFE53、0xFE59、0xFE61、0xFE66、0xFE67、0xFE6C、0xFE6D、0xFE76、0xFE7E、 0xFE90、0xFE91、0xFEA0)。4區(qū)的14個漢字在Unicode 5.0中其實(shí)也可以找到非PUA的編碼,詳見《Unicode、GB2312、GBK和GB18030中的漢字》。但按照GB18030,它們還是應(yīng)該映射到PUA碼位。
2.3 GB18030-2000和GB18030-2005的區(qū)別及以后版本
GB18030-2005與GB18030-2000的編碼體系結(jié)構(gòu)是完全相同的。GB18030-2005相對于GB18030-2000主要有以下變化:
1. 在四字節(jié)字符表中增加CJK統(tǒng)一漢字?jǐn)U充B和已經(jīng)在GB13000中編碼的我國少數(shù)民族文字字符的字形。其實(shí)GB18030-2000已經(jīng)映射了這些碼位,但GB18030-2000沒有給出這些字符的字形。
2. 調(diào)整字符的編碼。
其中的編碼調(diào)整比較有意思。的GB18030編碼是0xA8BC,在Unicode 5.0的編碼是0x1E3F。在GB18030-2000中0xA8BC被映射到Unicode的0xE7C7,因?yàn)殡p字節(jié)部分沒有映射0x1E3F,所 以它作為BMP的未映射字符被放到四字節(jié)部分的0x8135F437。GB18030-2005將0xA8BC映射到0x1E3F,那么Unicode碼 位0xE7C7怎么辦呢?為了最小化對原來編碼的影響,設(shè)計(jì)者將Unicode碼位0xE7C7映射到本來映射0x1E3F的0x8135F437。
GB18030已經(jīng)映射了Unicode的所有碼位,所以不管Unicode怎么變化,GB18030不過就是在現(xiàn)在的碼位上增加一些字形而已,編碼不會變化。只有現(xiàn)在還映射到PUA的24個字符以后可能會調(diào)整到非PUA碼位。調(diào)整方法應(yīng)該與的調(diào)整方法相同。
2.4 GB18030雙字節(jié)部分
前面已經(jīng)介紹過GB18030雙字節(jié)部分與GBK的區(qū)別,本小節(jié)再提一些細(xì)節(jié)。前面也說過,GB18030映射了 Unicode除代理區(qū)外的所有碼位。所以,Unicode BMP的6400個PUA碼位在GB18030中都有對應(yīng)的碼位。GB18030雙字節(jié)部分映射了2067個PUA碼位。
前面說過,GBK映射了2149個PUA碼位。現(xiàn)在GB18030雙字節(jié)部分映射了2067個PUA碼位。所以有 2149-2067=82個字符的映射發(fā)生了變化。GBK原來有95個字符映射到PUA,其中81個字符在GB18030中被映射到非PUA碼位。余下的 14個漢字就是《Unicode、GB2312、GBK和GB18030中的漢字》提到的那14個漢字(0xFE51、0xFE52、0xFE53、0xFE59、0xFE61、0xFE66、0xFE67、0xFE6C、0xFE6D、0xFE76、0xFE7E、0xFE90、0xFE91、0xFEA0)。附件1列出了這些字符的編碼變化。82個映射變化的碼位,除了這81個外,還有一個就是歐元符號:GB18030編碼是0xA2E3,Unicode編碼是0x20AC。碼位0xA2E3在GBK中被映射到0xE76C,GBK的碼位0xA2E3沒有定義字符。
GB18030雙字節(jié)部分與Unicode的映射沒有規(guī)律,只能通過查表方法映射。
2.5 GB18030四字節(jié)部分
GB18030四字節(jié)部分的字符可以見GB18030-2005的“表3 四字節(jié)部分的碼位安排”,一共54531個字符。GB18030四字節(jié)部分的碼位可以見GB18030-2005的“7.3 四字節(jié)部分字符的排列順序”。其中定義字符的只有兩個區(qū)域:
· GB18030用碼位0x81308130~0x8439FE39共50400個碼位映射該標(biāo)準(zhǔn)單字節(jié)和雙字節(jié)部分沒有映射過的39420個Unicode BMP碼位。
· GB18030用碼位0x90308130~0xE339FE39共1058400個碼位映射Unicode 16個輔助平面(平面1到平面16)的65536*16=1048576個碼位。
為了敘述方便,本文將0x81308130~0x8439FE39稱作“BMP擴(kuò)展部分”,將 0x90308130~0xE339FE39稱作“輔助平面部分”。GB18030四字節(jié)部分的碼位空間是0x81308130~0xFE39FE39。 第二字節(jié)有(0x39-0x30+1)=10個可能值。第三字節(jié)有(0xFE-0x81+1)=126個可能值。第四字節(jié)也是(0x39-0x30+1) =10個可能值。為了方便下面的演算,本文為這個碼位空間定義幾個名詞:
· 我們將四字節(jié)碼位空間中第一字節(jié)相同的區(qū)域稱作一級區(qū)。每個一級區(qū)有12600個碼位,即:10*126*10。
· 我們將四字節(jié)碼位空間中第一字節(jié)和第二字節(jié)相同的區(qū)域稱作二級區(qū)。每個二級區(qū)有1260個碼位,即:126*10。
· 我們將四字節(jié)碼位空間中前三個字節(jié)相同的區(qū)域稱作三級區(qū),每個三級區(qū)有10個碼位。
四字節(jié)部分一共有(0xFE-0x81+1)=126個一級區(qū)。BMP擴(kuò)展部分有4個一級區(qū)。輔助平面部分有84個一級區(qū)。還有38個一級區(qū)是保留區(qū)或自定義區(qū)。
2.5.1 BMP擴(kuò)展部分
BMP擴(kuò)展部分占據(jù)四字節(jié)部分開頭的4個一級區(qū),一共有4*12600=50400個碼位。這段空間的 Unicode映射說起來還是很簡單的,就是順序映射單字節(jié)、雙字節(jié)沒有映射過的BMP碼位。這些映射關(guān)系在GB18030-2000中確定下來。以后的 調(diào)整(例如)只是個別字符,不會影響其它字符的位置。但是因?yàn)殡p字節(jié)字符已經(jīng)映射過的BMP碼位沒有什么規(guī)律,所以造成BMP擴(kuò)展部分的Unicode 映射也不能用公式換算,還是要查表解決。
顯然這50400個碼位中只用到了39420個碼位,其余碼位都是保留的。出于好玩,我們來計(jì)算一下最后一個非保留碼位(0xFFFF)的位置,計(jì)算過程如下:
· m1=(39420-1)/12600=3
· n1=(39420-1)%12600=1619
· m2=n1/1260=1619/1260=1
· n2=n1%1260=1619%1260=359
· m3=n2/10=359/10=35
· n3=n2%10=359%10=9
· 第一字節(jié)的位置是:0x81+m1=0x81+3=0x84
· 第二字節(jié)的位置是:0x30+m2=0x30+1=0x31
· 第三字節(jié)的位置是:0x81+m3=0x81+35=0xA4
· 第四字節(jié)的位置是:0x30+n3=0x30+9=0x39
所以Unicode編碼0xFFFF映射的GB18030碼位是0x8431A439。在BMP擴(kuò)展部分中,0x8431A439以后的碼位都是保留碼位。上述計(jì)算中,/表示整除(例如5/3=1),%表示取余(例如5%3=2)。
2.5.2 輔助平面部分
輔助平面部分用84個一級區(qū)(0x90308130~0xE339FE39)直接映射Unicode的16個輔助平面。這部分映射是可以直接用公式計(jì)算的。讓我們看看怎么計(jì)算。
· 從Unicode編碼到GB18030編碼的映射方法如下:
· U=Unicode編碼-0x10000
· m1=U/12600
· n1=U%12600
· m2=n1/1260
· n2=n1%1260
· m3=n2/10
· n3=n2%10
· 第一字節(jié)b1=m1+0x90
· 第二字節(jié)b2=m2+0x30
· 第三字節(jié)b3=m3+0x81
· 第四字節(jié)b4=n3+0x30
按 照上述方法可以計(jì)算出0x10FFFF被映射到0xE3329A35。在輔助平面部分,0xE3329A35以后的碼位都是保留碼位。以上所寫的算法可以很容易寫成C/C++代碼。對于不會編程的讀者,也可以用Excel公式計(jì)算。假設(shè)Unicode編碼放在單元格A12,計(jì)算方法如下:
· 將m1放在B12,B12=INT((HEX2DEC(A12)-65536)/12600)
· 將n1放在C12,C12=MOD((HEX2DEC(A12)-65536),12600)
· 將m2放在D12,D12=INT(C12/1260)
· 將n2放在E12,E12=MOD(C12,1260)
· 將m3放在F12,F12=INT(E12/10)
· 將n3放在G12,G12=MOD(E12,10)
· 將第一字節(jié)放在H12,H12=DEC2HEX(B12+144)
· 將第二字節(jié)放在I12,I12=DEC2HEX(D12+48)
· 將第三字節(jié)放在J12,J12=DEC2HEX(F12+129)
· 將第四字節(jié)放在K12,K12=DEC2HEX(G12+48)
附件3中有寫好上述公式的Excel表格。使用函數(shù)HEX2DEC/DEC2HEX需要通過“工具->加載宏”鉤上“分析工具庫”。
· 從GB18030編碼到Unicode編碼的映射方法如下:
· 設(shè)GB18030編碼的四個字節(jié)依次為:b1、b2、b3、b4,則
Unicode編碼=0x10000+(b1-0x90)*12600+(b2-0x30)*1260+(b3-0x81)*10+b4-0x30
假設(shè)b1、b2、b3、b4分別放在A4、B4、C4、D4,Unicode編碼放在E4,則Excel計(jì)算公式為:
· E4 = =DEC2HEX((HEX2DEC(A4)-144)*12600+(HEX2DEC(B4)-48)*1260+(HEX2DEC(C4)-129)*10+(HEX2DEC(D4)-48)+65536)
2.6 GB18030和Unicode的映射表
附件3給出了GB18030和Unicode的映射表。這個Excel文件是在網(wǎng)友謝振斌先生的映射表基礎(chǔ)上制作的,包含3張表格:
1. 雙字節(jié)部分23940個碼位與Unicode的映射。兩組數(shù)據(jù)分別按GB18030和Unicode排序。
2. BMP擴(kuò)展部分39420個碼位與Unicode的映射。兩組數(shù)據(jù)分別按GB18030和Unicode排序。
3. 輔助平面部分,GB18030編碼和Unicode編碼的映射公式。
3 GB2312、GBK和GB18030中的圖形符號
在研究GB18030編碼的過程中,我整理了GB2312、GBK和GB18030在1區(qū)和5區(qū)的圖形符號,制作了附件4。這個Excel文件包含3張表格:
1. GB2312的1區(qū)字符表。GBK和GB18030的1區(qū)、5區(qū)字符表。用不同顏色標(biāo)注了GBK增加的35個字符和GB18030增加的11個字符。
2. GB2312 1區(qū)682個符號的編碼。
3. GBK 1區(qū)717個符號的編碼。
結(jié)束語
通過本文的介紹,讀者可以回答開頭的問題了嗎?
無論是Windows XP還是Vista,中文(中國)區(qū)域?qū)?yīng)的默認(rèn)代碼頁還是GBK。我們只能設(shè)置區(qū)域,并不能設(shè)置區(qū)域?qū)?yīng)的默認(rèn)代碼頁。所以在Windows世界,只要微軟不愿意,GB18030就只是一張普通的代碼頁。目前的簡體中文文檔使用的編碼主要是Unicode和GBK,應(yīng)該沒有什么文檔會用GB18030保 存。本文只是出于程序員的好奇而對GB18030編碼所作的一些研究,希望能對同樣好奇的讀者有所助益。