GB18030有两个版本:GB18030-2000和GB18030-2005。在本文中,没有指明版本的GB18030是指GB18030-2005。本文讨论了以下问题:
在讨论这些问题前,我们先约定一下码位空间的表示方法。
GBK是双字节编码,每个字符用两个字节表示。GB18030是多字节字符集,它的字符可以用一个、两个或四个字节表示。码位空间由各字节的范围确定。例如:GB18030的四字节字符码位空间是:
为了表述方便,我们用0x81308130~0xFE39FE39表示这个码位空间。也就是说:在本文中0x81308130~0xFE39FE39所指的并不是从0x81308130到0xFE39FE39的连续2097773834(0xFE39FE39-0x81308130+1)个字节。在本文中,0x81308130~0xFE39FE39所指的是编码的各字节在对应范围内的码位空间,这个码位空间的码位数目是:
(0xFE-0x81+1)*(0x39-0x30+1)*(0xFE-0x81+1)*(0x39-0x30+1)=126*10*126*10=1587600
同理,0xB0A1~0xF7FE代表的码位空间是第一字节在0xB0~0xF7之间,第二字节在0xA1~0xFE之间的所有码位。这个码位空间的码位数目是:
(0xF7-0xB0+1)*(0xFE-0xA1+1)=72*94=6768
这个码位空间就是GBK和GB18030的2区,在这6768个码位中定义了6763个字符。
本文用~表示上述码位空间,用-表示一般的范围,即:
读者如果已经理解了上面的约定,请完成下面两个习题:
以下是习题0.2的答案:
GB18030双字节字符的码位空间就是0x8140~0xFE7E和0x8180~0xFEFE,双字节字符的码位数目是7938+16002=23940。0x8140~0xFE7E和0x8180~0xFEFE也是GBK的全部码位空间。GBK在这23940个码位中定义了21886个字符。
GBK是双字节编码方案。它的码位空间就是前面所说的0x8140~0xFE7E和0x8180~0xFEFE,一共23940个码位。在这23940个码位上定义了21886个字符,包括21003个汉字和883个图形符号。《Unicode、GB2312、GBK和GB18030中的汉字》详细讨论了这21003个汉字。 本文的第3节会讨论GB2312、GBK和GB18030的图形符号。
GBK的码位空间可以划分为以下区域:
类别 | 区名 | 码位范围 | 码位数 | 字符数 |
符号区 | 1区 | 0xA1A1~0xA9FE | 846 | 717 |
5区 | 0xA840~0xA97E和0xA880~0xA9A0 | 192 | 166 | |
汉字区 | 2区 | 0xB0A1~0xF7FE | 6768 | 6763 |
3区 | 0x8140~0xA07E和0x8180~0xA0FE | 6080 | 6080 | |
4区 | 0xAA40~0xFE7E和0xAA80~0xFEA0 | 8160 | 8160 | |
用户自定义区 | 用户区1 | 0xAAA1~0xAFFE | 564 | |
用户区2 | 0xF8A1~0xFEFE | 658 | ||
用户区3 | 0xA140~0xA77E和0xA180~0xA7A0 | 672 |
我制作了一个Excel文件:附件1。这个文件包含3张表格:
GBK的23940个码位定义了21886个字符,还有23940-21886=2054个空闲码位,这2054个码位都被映射到Unicode的PUA。在设计GBK时,GBK的21886个字符中有95个在Unicode中没有对应字符,所以这95个字符也被映射到Unicode的PUA。在GBK的23940个码位中,一共有2054+95=2149个码位被映射到PUA,对应的PUA编码是0xE000-0xE864。0xE000-0xE864就是2149个码位。这2149个码位的分配有以下规律:
码位所在区域 | 码位数量 | 映射到的PUA范围 |
用户区1:0xAAA1~0xAFFE | 564 | 0xE000-0xE233 |
用户区2:0xF8A1~0xFEFE | 658 | 0xE234-0xE4C5 |
用户区3:0xA140~0xA77E和A180-A7A0 | 672 | 0xE4C6-0xE765 |
符号区(1区和5区)的170个空闲码位 | 170 | 0xE766-0xE80F |
2区的5个空闲码位:0xD7FA-0xD7FE | 5 | 0xE810-0xE814 |
4区的80个Unicode当时没有定义的字符:FE50-FE7E和FE80-FEA0 | 80 | 0xE815-0xE864 |
附件2包含两张表格:
GB18030是多字节字符集,它的字符可以用一个、两个或四个字节表示。GB18030的码位定义如下:
字节数 | 码位空间 | 码位数 | 字符数 |
单字节 | 0x00~0x7F | 128 | 128 |
双字节 | 0x8140~0xFE7E和0x8180~0xFEFE | 23940 | 21897 |
四字节 | 0x81308130~0xFE39FE39 | 1587600 | 54531 |
GB18030有128+23940+1587600=1611668个码位。Unicode的码位数目是0x110000(1114112),少于GB18030。所以,GB18030有足够的空间映射Unicode的所有码位。
GB18030的1611668个码位目前定义了128+21897+54531=76556个字符。Unicode 5.0定义了99089个字符。
GB18030编码可以分为:单字节部分、双字节部分和四字节部分。单字节部分与Unicode的0x00-0x7f完全相同。双字节部分与GBK有两点差异:
Unicode的BMP一共有65536个码位。其中代理区(0xD800-0xDFFF)有2048个码位,这2048个码位是不能定义字符的。GB18030的单字节部分映射了128个码位,GB18030的双字节部分映射了23940个码位。还剩下65536-2048-128-23940=39420个码位。
GB18030将这39420个码位顺序映射到从0x81308130开始的码位空间。GB18030将Unicode的16个辅助平面(0x10000-0x10FFFF,一共1048576个码位)顺序映射到从0x90308130开始的码位空间。GB18030四字节部分中只有这两个区域定义了字符,其它空间都是保留区和自定义区。本文的第3节和第4节还会详细讨论GB18030的双字节和四字节部分。
GB18030的设计思路可以概括到以下几点:
在GB18030目前定义的76556个字符中,只有24个字符被定义到Unicode的PUA区。这24个字符包括1区的10个竖排标点符号(0xA6D9-0xA6DF、0xA6EC-0xA6ED和0xA6F3)和4区的14个汉字(0xFE51、0xFE52、0xFE53、0xFE59、0xFE61、0xFE66、0xFE67、0xFE6C、0xFE6D、0xFE76、0xFE7E、0xFE90、0xFE91、0xFEA0)。4区的14个汉字在Unicode 5.0中其实也可以找到非PUA的编码,详见《Unicode、GB2312、GBK和GB18030中的汉字》。但按照GB18030,它们还是应该映射到PUA码位。
GB18030-2005与GB18030-2000的编码体系结构是完全相同的。GB18030-2005相对于GB18030-2000主要有以下变化:
其中的编码调整比较有意思。的GB18030编码是0xA8BC,在Unicode 5.0的编码是0x1E3F。 在GB18030-2000中0xA8BC被映射到Unicode的0xE7C7,因为双字节部分没有映射0x1E3F,所以它作为BMP的未映射字符被放到四字节部分的0x8135F437。GB18030-2005将0xA8BC映射到0x1E3F,那么Unicode码位0xE7C7怎么办呢?为了最小化对原来编码的影响,设计者将Unicode码位0xE7C7映射到本来映射0x1E3F的0x8135F437。
GB18030已经映射了Unicode的所有码位,所以不管Unicode怎么变化,GB18030不过就是在现在的码位上增加一些字形而已,编码不会变化。只有现在还映射到PUA的24个字符以后可能会调整到非PUA码位。调整方法应该与的调整方法相同。
前面已经介绍过GB18030双字节部分与GBK的区别,本小节再提一些细节。前面也说过,GB18030映射了Unicode除代理区外的所有码位。 所以,Unicode BMP的6400个PUA码位在GB18030中都有对应的码位。GB18030双字节部分映射了2067个PUA码位。
前面说过,GBK映射了2149个PUA码位。现在GB18030双字节部分映射了2067个PUA码位。所以有2149-2067=82个字符的映射发生了变化。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双字节部分与Unicode的映射没有规律,只能通过查表方法映射。
GB18030四字节部分的字符可以见GB18030-2005的“表3 四字节部分的码位安排”,一共54531个字符。GB18030四字节部分的码位可以见GB18030-2005的“7.3 四字节部分字符的排列顺序”。其中定义字符的只有两个区域:
为了叙述方便,本文将0x81308130~0x8439FE39称作“BMP扩展部分”,将0x90308130~0xE339FE39称作“辅助平面部分”。GB18030四字节部分的码位空间是0x81308130~0xFE39FE39。第二字节有(0x39-0x30+1)=10个可能值。第三字节有(0xFE-0x81+1)=126个可能值。第四字节也是(0x39-0x30+1)=10个可能值。为了方便下面的演算,本文为这个码位空间定义几个名词:
四字节部分一共有(0xFE-0x81+1)=126个一级区。BMP扩展部分有4个一级区。辅助平面部分有84个一级区。还有38个一级区是保留区或自定义区。
BMP扩展部分占据四字节部分开头的4个一级区,一共有4*12600=50400个码位。这段空间的Unicode映射说起来还是很简单的,就是顺序映射单字节、双字节没有映射过的BMP码位。这些映射关系在GB18030-2000中确定下来。以后的调整(例如)只是个别字符,不会影响其它字符的位置。但是因为双字节字符已经映射过的BMP码位没有什么规律,所以造成BMP扩展部分的Unicode映射也不能用公式换算,还是要查表解决。
显然这50400个码位中只用到了39420个码位,其余码位都是保留的。出于好玩,我们来计算一下最后一个非保留码位(0xFFFF)的位置,计算过程如下:
所以Unicode编码0xFFFF映射的GB18030码位是0x8431A439。在BMP扩展部分中,0x8431A439以后的码位都是保留码位。上述计算中,/表示整除(例如5/3=1),%表示取余(例如5%3=2)。
辅助平面部分用84个一级区(0x90308130~0xE339FE39)直接映射Unicode的16个辅助平面。这部分映射是可以直接用公式计算的。让我们看看怎么计算。
附件3给出了GB18030和Unicode的映射表。这个Excel文件是在网友谢振斌先生的映射表基础上制作的,包含3张表格:
在研究GB18030编码的过程中,我整理了GB2312、GBK和GB18030在1区和5区的图形符号,制作了附件4。这个Excel文件包含3张表格:
通过本文的介绍,读者可以回答开头的问题了吗?
无论是Windows XP还是Vista,中文(中国)区域对应的默认代码页还是GBK。我们只能设置区域,并不能设置区域对应的默认代码页。 所以在Windows世界,只要微软不愿意,GB18030就只是一张普通的代码页。目前的简体中文文档使用的编码主要是Unicode和GBK,应该没有什么文档会用GB18030保存。本文只是出于程序员的好奇而对GB18030编码所作的一些研究,希望能对同样好奇的读者有所助益。