
国标码与机内码转换
-
2023年3月20日发(作者:核污染)刨根究底字符编码之六——简体汉字编码中区位码、国标码、内
码、外码、字形码的区别及关系
简体汉字编码中区位码、国标码、内码、外码、字形码的区别及
关系
GB2312、GBK、GB18030等GB类汉字编码⽅案的具体实现⽅式是怎样的?区位码是什么?国标码是什么?内码、外码、字形码⼜是什么
意思?它们是如何转换的,⼜为什么要这样转换?
下⾯以GB2312为例来加以说明(由于GBK、GB18030是以GB2312为基础扩展⽽来,因此编码实现⽅式与GB2312⼀样)。
⼀、区位码
1.
整个GB2312字符集分成94个区,每区有94个位,每个区位上只有⼀个字符,即每区含有94个汉字或符号,⽤所在的区和位来对字符进⾏编
码(实际上就是字符编号、码点编号),因此称为区位码(或许叫“区位号”更为恰当)。
换⾔之,GB2312将包括汉字在内的所有字符编⼊⼀个94*94的⼆维表,⾏就是“区”、列就是“位”,每个字符由区、位唯⼀定位,其对应的
区、位编号合并就是区位码。⽐如“万”字在45区82位,所以“万”字的区位码是:4582(注意,GB类汉字编码为双字节编码,因此,45相当
于⾼位字节,82相当于低位字节)。
2.
GB2312字符集中:
1)01~09区(682个):特殊符号、数字、英⽂字符、制表符等,包括拉丁字母、希腊字母、⽇⽂平假名及⽚假名字母、俄语西⾥尔字母
等在内的682个全⾓字符;
2)10~15区:空区,留待扩展;
3)16~55区(3755个):常⽤汉字(也称⼀级汉字),按拼⾳排序;
4)56~87区(3008个):⾮常⽤汉字(也称⼆级汉字),按部⾸/笔画排序;
5)88~94区:空区,留待扩展。
⼆、国标码(交换码)
1.
为了避开ASCII字符中的不可显⽰字符00000000~00011111(⼗六进制为0~1F,⼗进制为0~31)及空格字符00100000(⼗六进制为20,
⼗进制为32)(⾄于为什么要避开、⼜为什么只避开ASCII中0~32的不可显⽰字符和空格字符,后⽂有解释),国标码(⼜称为交换码)规定表
⽰汉字的范围为(00100001,00100001)~(01111110,01111110),⼗六进制为(21,21)~(7E,7E),⼗进制为(33,33)~(126,126)
(注意,GB类汉字编码为双字节编码)。
因此,必须将“区码”和“位码”分别加上32(⼗六进制为20H,后缀H表⽰⼗六进制),作为国标码。也就是说,国标码相当于将区位码向后偏移
了32,以避免与ASCII字符中0~32的不可显⽰字符和空格字符相冲突。
2.
注意,国标码中是分别将区位码中的“区”和“位”各⾃加上32(20H)的,因为GB2312是DBCS双字节字符集,国标码属于双字节
码,“区”和“位”各作为⼀个单独的字节。
这样我们可以算出“万”字的国标码⼗进制为:(45+32,82+32)=(77,114),⼗六进制为:(4D,72H),⼆进制为:(01001101,0111
0010)。
(笨笨阿林原创⽂章,转载请注明出处)
三、内码(机内码)
1.
不过国标码还不能直接在计算机上使⽤,因为这样还是会和早已通⽤的ASCII码冲突(导致乱码)。
⽐如,“万”字国标码中的⾼位字节77与ASCII的“M”冲突,低位字节114与ASCII的“r”冲突。因此,为避免与ASCII码冲突,规定国标码中的每
个字节的最⾼位都从0换成1,即相当于每个字节都再加上128(⼗六进制为80,即80H;⼆进制为10000000),从⽽得到国标码的“机内码”表
⽰,简称“内码”。
2.
由于ASCII码只⽤了⼀个字节中的低7位,所以,这个⾸位(最⾼位)上的“1”就可以作为识别汉字编码的标志,计算机在处理到⾸位是“1”的编码
时就把它理解为汉字,在处理到⾸位是“0”的编码时就把它理解为ASCII字符。
⽐如:
77+128=205(⼆进制为11001101,⼗六进制为CD)
114+128=242(⼆进制为11110010,⼗六进制为F2)
3.
我们可以来检验⼀下。打开记事本输⼊“万”字,编码选择为ANSI(Windows记事本中的ANSI编码对于简体汉字⽽⾔就是GB类编码,详见后⽂
解释),保存,如下图所⽰。
然后⽤⼆进制编辑器(⽐如UltraEdit)打开刚才保存的⽂件,切换到⼗六进制模式,会看到:CDF2,这就是“万”字的内码,如下图所⽰。
4.
总结⼀下:
从区位码(国家标准定义)--->区码和位码分别+32(即+20H)得到国标码--->再分+128(即+80H)得到机内码(与ACSII码不再冲突)。
因此,区位码的区和位分别+160(即+A0H,32+128=160)可直接得到内码。⽤⼗六进制表⽰就是:
区位码(区码,位码)+(20H,20H)+(80H,80H)=区位码(区码,位码)+(A0H,A0H)=内码(⾼字节,低字节)。
四、为什么要加上20H和80H?
1.
区位码、国标码、内码的转换⾮常简单,但是令⼈迷惑的是为什么要这么转换?
⾸先,需要注意到⼀点,GB2312虽说是对中⽂编码,但是⾥⾯也有对26个英⽂字母和⼀些特殊符号的编码,按理说这些和ASCII重合的字
符(33~127)应该⽆需再重新编码,直接沿⽤ASCII中的不就⾏了?
2.
原来,当时在制定GB2312时,决定对ASCII中的可打印字符,也就是英⽂字母、数字和符号部分(33~126,127为不可打印的DEL)重新编⼊
GB2312中,以两个字节表⽰,称之为全⾓字符(全⾓字符在屏幕上的显⽰宽度为ASCII字符的两倍,后来也因此⽽将对应的ASCII字符称之为
半⾓字符)。
⽽对于ASCII中前32个不可显⽰也不可打印的控制字符(ASCII码为0~31),以及第33个可显⽰但不可打印的空格字符(ASCII码为32)等共33个
不可打印字符的编码则直接沿⽤,不再重新编码。
3.
因为要保留这33个不可打印字符,就不能直接采⽤区位码作为计算机直接处理的机内码,需要将区位码向后偏移32以避开冲突(为什么是偏
移32,⽽不是偏移33?因为区位码中的区码和位码都是从1开始计数的,不像ASCII码是从0开始计数的)。
⼗进制数字32的⼗六进制表⽰就是20(为区别于⼗进制,记作20H),这也就是区位码要加上20H(区码和位码各⾃加上20H)才能得到国标码的
原因。
(笨笨阿林原创⽂章,转载请注明出处)
3.
很显然,如果直接采⽤国标码作为计算机直接处理的机内码的话,还将会产⽣另⼀个弊端,即⽤ASCII码编码的英⽂字符在GB2312编码环
境中⽆法打开,⼀打开就会乱码。
因为国标码虽然相较于区位码避开了ASCII码中0~32的前33个不可打印字符,但并没有避开ASCII码中的英⽂字母、数字和符号(33~126,
共94个字符,127为不可打印的DEL)等可打印字符。也就是说,国标码并不是完全兼容ASCII码的。
4.
为了解决这个弊端,考虑到ASCII码只使⽤了⼀个字节中的低7位,最⾼位(即⾸位)为0,于是决定将国标码每个字节的最⾼位设为1(国标码的
两个字节中的最⾼位都恒为0,即国标码中的每个字节实际上也只⽤了⼀个字节中的低7位),这就是GB2312的机内码(即内码),简称
GB2312码。
这样⼀来就彻底区分开了ASCII码和GB2312码。这也是为什么国标码还要加上(80H,80H)才能得到机内码的原因。
5.
看到这⾥,有⼈或许⼜要问了:如果仅仅是为了避免与ASCII编码相冲突,为什么最初不直接将区位码的区码和位码的最⾼位从0改为1(相当
于各⾃直接加上128),这样不就⽆需经过国标码多此⼀举的中间转换了吗?⽽且还⽆需后移32,也就不⽤浪费这部分编码空间。
对此本⼈也很困惑,在⽹上搜了很久也没找到答案,因此具体原因不得⽽知。或许是⼀开始考虑不周?或许为了未来扩展所需⽽预留⼀部分
空间?⼜或许是有其他不得已的原因?有知道的朋友还望能指点迷津。
GB2312区位码、国标码、内码对照表(其中汉字内码B0A1~F7FE,共6763个)
五、外码(输⼊码、输⼊法编码)
1.
外码也叫输⼊码、输⼊法编码,是⽤来将汉字输⼊到计算机中的⼀组键盘符号,是作为汉字输⼊⽤的编码。
英⽂字母只有26个,可以把所有的字符都放到键盘上,⽽使⽤这种办法把所有的汉字都放到键盘上,是不可能的。所以汉字系统需要有⾃⼰
的输⼊码体系,使汉字与键盘能建⽴对应关系。
2.
⽬前常⽤的外码分为以下⼏类:
1)数字编码,⽐如区位码;
2)拼⾳编码,⽐如全拼、双拼、⾃然码等;
3)字形编码,⽐如五笔、表形码、郑码等。
六、字形码(字型码、字模码、输出码)
1.
字形码,⼜称为字型码、字模码、输出码,属于点阵代码的⼀种。
为了将汉字在显⽰器或打印机上输出,把汉字按图形符号设计成点阵图,就得到了相应的点阵代码(字形码)。
也就是⽤0、1表⽰汉字的字形,将汉字放⼊n⾏*n列的正⽅形(点阵)内,该正⽅形共有n^2个⼩⽅格,每个⼩⽅格⽤⼀位⼆进制表⽰,凡是笔
划经过的⽅格值为1,未经过的值为0。
2.
显⽰⼀个汉字⼀般采⽤16×16点阵或24×24点阵或48×48点阵。已知汉字点阵的⼤⼩,可以计算出存储⼀个汉字所需占⽤的字节空间。
⽐如,⽤16×16点阵表⽰⼀个汉字,就是将每个汉字⽤16⾏,每⾏16个点表⽰,⼀个点需要1位⼆进制代码,16个点需⽤16位⼆进制代码(即
2个字节),共16⾏,所以需要16⾏×2字节/⾏=32字节,即16×16点阵表⽰⼀个汉字,字形码需⽤32字节。
因此,字节数=点阵⾏数×(点阵列数/8)。
3.
显然,字形码所表⽰的字符,相对于抽象字符表ACR⾥的“抽象”字符,可称之为“具体”字符,因为具有了“具体”的外形。
4.
为了将汉字的字形显⽰输出或打印输出,汉字信息处理系统还需要配有汉字字形库,也称字模库,简称字库,它集中了汉字的字形信息。
字库按输出⽅式可分为显⽰字库和打印字库。⽤于显⽰输出的字库叫显⽰字库,⼯作时需调⼊内存。⽤于打印输出的字库叫打印字库,⼯作
时⽆需调⼊内存。
字库按存储⽅式也可分为软字库和硬字库。软字库以⽂件的形式存放在硬盘上,现多⽤这种⽅式。硬字库则将字库固化在⼀个单独的存储芯
⽚中,再和其它必要的器件组成接⼝卡,插接在计算机上,通常称为汉卡。这种⽅式现已淘汰。
七、⼩结
可以这样理解,为在计算机内表⽰汉字⽽采取统⼀的编码⽅式所形成的汉字编码叫内码。为⽅便汉字输⼊⽽形成的汉字编码为外码,也叫输
⼊码。为显⽰输出和打印输出汉字⽽形成的汉字编码为字形码,也称为字模码、输出码。
计算机通过键盘输⼊的外码(重码时还需附加选择编号)对应于汉字内码,将汉字外码转换(即映射)为汉字内码,以实现输⼊汉字的⽬的;通过
汉字内码在字模库(即字库)中找出汉字的字形码,将汉字内码转换(即映射)为汉字字形码,以实现显⽰输出和打印输出汉字的⽬的。
事实上,英⽂字符的输⼊、处理和显⽰过程⼤致上也差不多,只不过英⽂字符不需要输⼊码(即外码),直接在键盘上输⼊对应的英⽂字母即
可。
(笨笨阿林原创⽂章,转载请注明出处)
【预告:下⼀篇将重点剖析⾮常容易令⼈困惑的所谓ANSI编码与代码页(CodePage),敬请关注!】