计算机基础系列-编码与乱码


原创文章,转载请标注出处:《计算机基础系列-编码与乱码》


一、概述

编码这一块一直是一块心病,总是不清不楚的,说到底其实就是没有彻底了解了它的历史和原理。

了解了之后才发现,其实很简单的道理。

计算机需要依靠二进制来进行运作。而我们的世界是一个十进制通行,附带庞大复杂的各种字符的世界。要将自然界映射到计算机中,就必须定义一种二进制到字符的映射关系。

自然界的各种字符除了数字可以通过进制转换直接转换成为二进制之外,其他的字符都无法直接进行二进制转换,怎么办,只能强行进行字符映射,将自然界的每一个能搜罗到的字符都提供一个唯一的二进制映射。

有了这种映射关系,我们就能用计算机来模拟世界。

二、编码

计算机的编码有很多种,各种不同的编码所能映射的字符范围和字符数量并不相同,甚至还有地域之分。

2.1 ASCII

ASCII可以说是编码中的元老,虽然实现的字符范围很少,但却都是计算机中最常用的字符,因为它根本就是由美国来实现的最早的最实用的字符的映射编码。

ASCII提供了128个字符映射,使用二进制7位即可表示,所以ASCII使用的是一个字节的二进制位来映射的。

但ASCII只适用于美国本地,其他国家和民族拥有更多、更复杂的字符,仅仅这128个基本字符根本无法满足需要,既然如此,那我们就需要定义自己的字符映射编码了。

2.2 ISO 8859-1

ISO 8859-1是西欧国家定义的字符映射编码,它同样采用8位二进制来表示编码,8位总共可以表示0-255共256个字符,它的前128个字符与ASCII一样,之后的字符为西欧国家特有字符。

2.3 windows-1252

ISO 8859-1虽然说是西欧国家使用的字符编码,实质上暗里使用的却是windows-1252,这个编码是对ISO 8859-1的补充,它新增了包括欧元符号在内的一些常用字符,补充了ISO 8859-1的不足,已被西欧国家使用,虽然表面使用的是ISO 8859-1,其实编码解码用的都是windows-1252,即使是文件明确声明使用ISO 8859-1进行编码,但在解码的时候默认还是windows-1252。

所以windows-1252其实和ISO 8859-1是差不多的。

2.4 GB2312

说到中国,汉字是我们使用的文字,即字符,我们要做的就是将汉字纳入编码范围,最早的汉字编码就是GB2312,它由两个字节来表示,两个字节最高位均为1,如果为0,则为ASCII,可见在汉字编码中同样兼容了ASCII。

GB2312采用两个字节,并且其最高位都固定为1,只有剩余的14位供我们使用,这14个二进制位可以表示2的14次方(16384)个汉字字符,实际情况是GB2312收集了最常用的7000多简体汉字。

其中高位字节的使用范围为0XA1-0XF7(即:10100001-11110111),低位使用范围为0XA1-0XFE(即:10100001-11111110)。

2.5 GBK

GBK是在GB2312基础上建立的,新增了14000多个汉字,包括繁体字,那么它所表示的汉字字符数量就达到了21000多,明显超出了GB2312的最大范围16384,而且它使用的还仍然是2个字节来进行表示,如何实现的呢?其实GBK打破了GB2312中的一个限制,那就是低位字节的最高位不再限制为1,那么我们就多了一位来进行字符映射,可以映射的字符总量达到了2的15次方(32768)个,足够使用了。

这样来读取的时候,只要遇到高位为1的字节,一次性读取两个字节进行解码,如此一来,低位字节是否为1就无法影响到解码工作了。

其中高位字节的使用范围为0X81-0XFE(即:10000001-11111110),低位使用范围为0X40-0X7E(即:01000000-01111110)和0X80-0XFE(即:10000000-11111110)。

2.6 GB18030

GB18030又是在GBK基础上建立的,新增了55000多个字符,总字符量达到了71000多个,两个字节终于无法实现如此多的字符映射,为了兼容GBK,GB18030采用了变长编码。使用2个字节表示的就是GBK编码,新增的全部使用四个字节表示。

其中第一字节范围为0X81-0XFE(即:10000001-11111110),第二个字节范围为0X30-0X39(即:00110000-00111001),第三个字节范围同第一个字节,第四个字节范围同第二个字节。

那么解码的时候是如何区分是几个字节呢,关键就看第二个字节了,因为2个字节表示的GBK的第二个字节最小为0X40,大于四个字节表示的GB18030中的第二字节的最大值0X39。通过第二个字节的取值就能确定需要一次性读取2个字节还是4个字节。

2.7 Big5

Big5是繁体字符编码,使用于台湾,香港地区,使用2个字节表示。

其中高位字节范围为0X81-0XFE(即:10000001-11111110),低位字节范围为0X40-0X7E(即:01000000-01111110)和0XA1-0XFE(即:10100001-11111110)。

可将其视为与GB2312并列,前者表示繁体字符,后者表示简体字符。

2.8 其他

上面仅仅介绍了美国、西欧国家、中国的编码,世界上国家和民资何其多,每个国家都建立自己的编码,每种编码之间又不兼容,极不利于计算机的共享和互联网的建立。计算机急需一种能够代表世界上所有字符的编码来统一字符的编解码工作。这就触生了Unicode。

2.9 Unicode

Unicode的目的就是对世界上所有字符进行编码或标识,它对所有字符进行了编号。

Unicode编码包括两部分字符集,基本字符集和增补字符集,前者范围为U+0000-U+FFFF(即:0000 0000 0000 0000-1111 1111 1111 1111),后者范围为U+10000-U+10FFFF(即:0001 0000 0000 0000 0000-0001 0000 1111 1111 1111 1111)

不同于上面所述的编码,Unicode仅仅作了编号标识,却并没有定义如何进行二进制表示。为此专门设计了UTF-*系列二进制映射法则。

2.9.1 UTF-32

UTF-32采用四个字节来表示字符,由于Unicode编码同样采用四个字节来标识字符,所以,UTF-32直接使用Unicode编号的二进制形式来表示字符。

表示形式有两种,前高后低为大端(BE),前低后高为小端(LE)

所有字符全用四个字节表示,无疑极其浪费空间。

2.9.2 UTF-16

UTF-16作了一些优化,采用变长的方式来表示字符,对基本字符集中的字符直接采用两个字节来表示,增补字符集中的字符采用四个字节来表示,需要经过一个转换算法将Unicode的字符标识编号转化为此处的四个字节的二进制表示形式。

虽然节省了一点空间,但是对于美国和西欧国家那些只需要1个字节就能表示所有字符的国家来说,还是浪费,这样就有了UTF-8。

2.9.3 UTF-8

UTF-8也采用变长的方式来表示字符。

  • 对于Unicode编号范围0X00-0X7F(0-127)采用一个字节表示:0xxxxxxx(高位固定位0,可表示128个字符),对应ASCII。
  • 对于Unicode编号范围0X80-0X7FF(128-2047)采用两个字节表示:110xxxxx 10xxxxxx(首字节高位固定110,末字节高位固定10,剩余位可表示最多2048个字符)
  • 对于Unicode编号范围0X800-0XFFFF(2048-65535)采用三个字节表示:1110xxxx 10xxxxxx 10xxxxxx(首字节高位固定1110,其余字节首位固定10,剩余16位可表示65536个字符),对应汉字编码。
  • 对于Unicode编号范围0X10000-0X10FFFF(65536-1114111)采用四个字节表示:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx(首字节高位固定11110,其余字节首位固定10,剩余21位可表示2097152个字符),对应增补字符。

UTF-8兼容ASCII。

三、编码转换和乱码

Unicode统一编码出现之后,并不意味着以前各自定义的千奇百怪的编码方式就不用了,为了兼容Unicode,不得已只能在自己的编码中内置一个自己的编码二进制与Unicode中同字符编号的映射表,依靠这个映射表,可以实现编码的转换,将各种私有编码与Unicode统一编码进行来回转换。同时乱码出现了。

乱码就是编码转换使用了错误的编码方式导致的。

3.1 编码转换

所谓的编码转换就是将以某种编码表示某个字符的转换为另外一种编码表示,编码转换的基本是保证表示的字符不发生变化,这种转换一般发生在私有编码和Unicode编码之间。比如将GB18030编码表示的汉字字符转换为UTF-8表示的Unicode编码。这其中Unicode起到的是中转站的作用,它就是编码转换的核心。

所谓编码转换,即使再转换,它所代表的含义(字符)是不变的,不能因为编码转换将字符给变了。实质是在计算机内部保存的二进制发生了变化,使用哪种编码编码,就要使用相同的编码解码,否则就会出现乱码。

3.2 乱码

乱码就是编码和解码使用了不同的编码方式导致的。

乱码可以进行恢复,但是经过多次编码转换出现的乱码极难恢复,变数太多,如果是只经过一次,则可以经过逆向编码转换的方式尝试恢复。

备注:

猜你喜欢

转载自blog.csdn.net/weixin_34259159/article/details/87550374