1 前言
在计算机的内部,所有数据都是通过二进制编码而保存的,计算机只能识别二进制。因此我们存储任何信息到计算机中时,都会转换成相对应的二进制编码。
2 西文字符的编码表示——ASCII 码
ASCII 码即美国标准信息交换代码(America Standard Code for Information Interchange),由字面意思就能知道这个编码诞生的目的就是为了信息交换。ASCII 码对以下字符都进行了编码,其中包括:
- 大小写字母,共52个;
- 0~9 的数字,共10个;
- 控制符,如空格符、回车符等共34个字符;
- 通用符,如 +、-、*、/、!、? 等共32个字符。
一共128个,而7位二进制位刚好可以表示 0 ~ 127 一共128个十进制数,所以 ASCII 码中的每个字符只需7位二进制位(单位比特或 bit)即可表示。ASCII 码是目前计算机特别是微型计算机中最普遍使用的字符编码集。ASCII 码中常见的数字、字母的 ASCII 码和其对应的十进制、十六进制数如下表所示(控制字符用于格式控制,是不可见字符,除了不可见字符以外的字符都是可见字符。):
字符 | ASCII 码:二进制数(B) | 十进制数(D) | 十六进制数(H) |
---|---|---|---|
数字0~9 | 0011 0000B ~ 0011 1001B | 45 ~ 57 | 30H ~ 39H |
大写字母 A ~ Z | 0100 0001B ~ 0101 1010B | 65 ~ 90 | 41H ~ 5AH |
小写字母 a ~ z | 0110 0001B ~ 0111 1010B | 97 ~ 122 | 61H ~ 7AH |
我们知道,计算机中的地址和数据都是以字节为单位的,一个字节是8个二进制位,那就说明 ASCII 码中的每个字符实际上是占用一个字节。因此我们通常将高位为0的 0000 0000B ~ 0111 1111(0 ~ 127) 的 ASCII 码称为基本的 ASCII 码;而将高位为1的 1000 0000B ~ 1111 1111(128 ~ 255) 的 ASCII 码称为扩展的 ASCII 码。但不管是哪一种,它们都属于机内码的范畴。
3 中文字符的编码表示
3.1 区位码
由于汉字远多于英文字母,我们不能将所有的汉字都印在键盘上。所以对于汉字的编码通常是采用两个字节,每个字节各取7位,从而能够表示 2 7 × 2 7 = 16384 2^7\times 2^7=16384 27×27=16384个汉字。
中国国家标准总局于1980年发布的 GB2312-80 编码方案对汉字进行了分区处理。GB 即国标码,是计算机与其他系统或设备间交换汉字信息的标准编码。那么分区处理具体是什么意思呢?
这种编码方案首先将汉字分为一级汉字和二级汉字,一级汉字按拼音排序,二级汉字按部首排序。这些汉字被放置在一个 94 x 94 的方阵中,每一行称为一个“区”,每一列称为一个“位”,构成一个共有94区94位的字符集。
- 01 ~ 09区:682个非汉字图形字符;
- 10 ~ 15区:空区留待扩展;
- 16 ~ 55区:3755个常用汉字(一级汉字)字符,按拼音排序;
- 56 ~ 87区:3088个非常用汉字(二级汉字)字符,按部首排序;
在这个字符集中,每一个汉字都唯一对应一个区号和位号,各自可用一个字节来表示,从而构成了区位码。区位码 = 区号(1~94) + 位号(1~94),区号在高字节,位号在低字节。比如国处于25行90列,那么它的区位码就是2590,换为十六进制就是197AH,也就是其在计算机中的存储编码。
3.2 国标码
对于输入计算机的西文字符,计算机存储的是其对应字符的 ASCII 码。这些可被计算机识别、存储和运算的二进制代码被称为机内码,如大写字母“A”对应于十进制的65,然后转为二进制的 0100 0001B 存在计算机中。
区号和位号都是用一个字节来保存,这就会导致其与 ASCII 编码中的不可见字符(0x00 ~ 0x1F,即十进制的0~31)以及空格(0x20,即十进制的32)冲突,因此标准总局又将区号和位号分别加上32(十六进制的 20H)作为“国标码”。
国标码中区号和位号的范围如下表所示:
二进制(B) | 十进制(D) | 十六进制(H) |
---|---|---|
0010 0001B ~ 0111 1110B | 33 ~ 126 | 21H ~ 7EH |
- 国标码 = 十六进制的区位号 + 2020H;
- 国标码 = 十进制的区位号 + 32,再转为十六进制。
3.3 机内码
但是这个“国标码”仍然与 ASCII 编码方案中的其它字符(0x21~0x7F,即十进制的33~127)冲突,故又把“国标码”每个字节的最高位都由0变成1,这相当于每个字节再加上128(十六进制的 80H),从而得到“机内码”。其实相当于避开了所有基本的 ASCII 码。注意这个机内码特指汉字的机内码,简称“内码”,是对国标码的一个改进,而不是 3.2国标码 中开头提到的“机内码”。
例如“万”字区位码是1601,即 1001H,所以“国标码”是 3021H,“机内码”是 B0A1H。
- 机内码 = 区位码 + 160(A0H);
- 机内码 = 国标码 + 128(80H)。
为什么没有一步到位避开 ASCII 中的扩展字符呢?因为 GB2312-80 方案是1980年发布的,而 ASCII 方案直到1986年才定了最终版本。此后,计算机中使用 GB2312 编码方案时都采用上述机内码的方案,以兼容 ASCII 字符集,而所谓的国标码实际上就没有用了。
3.4 国标码与机内码
1995年中国国家标准总局年发布的《汉字内码扩展规范(GBK)》1.0版对 GB2312 标准进行了扩充,此时,仍然称 GB2312 为“机内码”。直到2000年 GB18030-2000 标准发布,取代了 GBK,这才摒弃“国标码”与“机内码”同存的称呼,与 ASCII 兼容的“机内码”成为名正言顺的“国标码”,GB2312 的老国标码弃用。
所以,现在提到汉字国标码,不要再去纠结什么机内码,请直接将 GB2312 及 GBK 所谓的机内码当做国标码对待。而且,2017年起,GB2312-80 标准已经转化为推荐性标准 GB/T 2312-1980,不再强制执行。再者,现在的中文处理多以国标 GB18030 和国际通用的 Unicode 编码方案执行,GB2312 及其扩展方案 GBK 逐渐会被淘汰。
总之,“机内码”指的是编码的一个分类,而非某一特定的编码方式。引起理解错误的主要原因是 GB2312 编码方案中提到的机内码与国标码的混乱。
目前,网络上描述机内码的文章(多为旧文)很多都将读者引导至 GB2312 中提到的概念,我们应该摒弃淡忘这个描述,正确理解机内码是一个分类而非特定的一种编码。提到 GB2312 编码,也应该理解 B0A1 就是“啊”的国标码而不是所谓的“机内码”,别无其它。
4 机外码
机内码:计算机传输存储汉字时使用的编码。国标码(GB2312/18030)中“汉”编码是“BABA”,国际码(Unicode)中“汉”编码是“6C49”等等,都是机内码。
机外码:即人向计算机输入汉字时在键盘上按键的编码。例如输入“汉”,拼音输入时按键 “han”,五笔输入时按键 “ic” 等等,都是机外码;
常见的几种外码如下:
- 数字编码,比如区位码;
- 拼音编码,比如全拼、双拼、自然码等;
- 字形编码,比如五笔、表行码、郑码等。
希望本篇博客能对你有所帮助,也希望看官能动动小手点个赞哟~~。