ASCII、Unicode、GBK、UTF-8的爱恨纠葛

769 阅读9分钟

0和1?

大家应该都知道,所有信息在计算机底层存储都是以二进制的形式,每一个二进制位(bit)有0和1两种状态.因此实际上存储的都是一连串的0和1.但是我们实际上看到的所有内容并不少0和1,可能是中文,可能是英文,可能是符号? 那么显然是计算机系统将底层的0和1通过某种规则映射成了我们能够认知的语言和符号. 那么问题来了, 到底有哪些映射规则呢?

字符,字符集,字符编码概念(注意区分概念)

字符:在计算机和电信技术中,一个字符是一个单位的字形、类字形单位或符号的基本信息。即一个字符可以是一个中文汉字、一个英文字母、一个阿拉伯数字、一个标点符号等。

字符集:多个字符的集合。

字符集的目的是完成映射, 映射完成后二进制数就变成了新的字符

字符编码:把字符集中的字符编码为(映射)指定集合中的某一对象,以便文本在计算机中存储和通过通信网络的传递

编码的目的是为了传输 编码会按某种规则将字符集和内容映射成不同的内容, 经过解码后恢复本身的含义(可以类比于加密解密)

ASCII(即是字符集又是编码)

上个世纪60年代,美国制定了一套字符集,对英语字符与二进制位之间的关系,做了统一规定。这被称为 ASCII(American Standard Code for Information Interchange,美国标准信息交换代码).

ASCII 码规定8位二进制表示一个符号, 即一长串二进制数字, 按照ASCII规定, 就会按8位分成一组, 逐个按照映射表, 表示相应的符号.

标准ASCII码

8位二进制中第一位(高位)一直为0 后面7位一共有2^7次, 128种不同的组合, 表示所有的大写和小写字母,数字0 到9、标点符号, 以及在美式英语中使用的特殊控制字符。

扩展ASCII码

标准ASCII码在美国使用基本没问题,但是一旦推广到其它国家, 他们有属于自己的字母和符号, 现有的符号无法满足需求, 因此其它国家就将8位二进制的第一位置位1, 然后用扩展出来的128种组合, 作为自己国家特殊符号的表示.

比如,在法语中,字母上方有注音符号,它就无法用 ASCII 码表示。于是,一些欧洲国家就决定,利用字节中闲置的最高位编入新的符号。比如,法语中的é的编码为130(二进制10000010)

ASCII码在传输的时候没有对字符集进行特殊处理,直接以字符集的形式传输,因此很多人也称ASCII编码,实际表示ASCII字符集

扩展ASCII码能满足大家的需求吗?

但是,这里又出现了新的问题。不同的国家有不同的字母,因此,哪怕它们都使用256个符号的方式,代表的字母却不一样。比如,130在法语编码中代表了é,在希伯来语编码中却代表了字母Gimel (ג),在俄语编码中又会代表另一个符号。但是不管怎样,所有这些编码方式中,0--127表示的符号是一样的,不一样的只是128--255的这一段。

至于亚洲国家的文字,使用的符号就更多了,汉字就多达10万左右。一个字节只能表示256种符号,肯定是不够的,就必须使用多个字节表达一个符号.

GBK(即是字符集又是编码)

由于ASCII字符集不支持中文,因此,当中国人用到计算机时,就需要寻求一种中文字符集。 于是,国人就定义了自己的字符集规则:

(GBK前身)GB2312

  1. 标准ASCII码继续支持,当字符小于127位时,与ASCII的字符相同.需要注意不支持扩展ASCII码的含义
  2. 一旦出现连续的两个8位二进制第一位为1的时候, 认为这两个8位二进制组合在一起表示一个中文,这样大约可以组合7000多个简体汉字。

GBK

在GB2312的基础上, 不再要求连续两个8位二进制第一位为1, 只要出现8位二进制数第一位为1,就认为连续的16位是一个整体, 就代表一个汉字,这样新增了近20000个新的汉字(包括繁体字)和符号。

GB18030

但是,中国有56个民族,所以,我们再次对编码规则进行了扩展,又加了近几千个少数民族的字符,于是再次扩展后得字符集叫做GB18030。

中国的程序员觉得这一系列字符集的标准是非常的好,于是统统称他们叫做"DBCS"(Double Byte Charecter Set 双字节字符集)。

GBK在传输的时候没有对字符集进行特殊处理,直接以字符集的形式传输,因此也称GBK编码,实际表示GBK字符集

通过这种方式, 很好的解决了中文字符集的问题, 但是不是还是各个国家自己玩自己的字符集吗, 要是有一天我的系统需要中文和法文了, 这种字符集不就凉凉了?

unicode字符集(不是编码方式)

因为世界国家很多,每个国家都定义一套自己的字符集,结果相互之间谁也不懂谁的,就无法进行很好的沟通交流,所以及时的出现了一个组织ISO(国际标准化组织)决定定义一套字符集来解决所有国家的编码问题,这个新的方案就叫做Unicode。unicode为每一个「字符」分配一个唯一的 ID(学名为码位 / 码点 / Code Point),可以将Unicode理解为一本世界编码的字典。

unicode本身并不限制多少个8位二进制数表示一个字符,例如标准的2字节形式通常称作UCS-2。然而,受制于2字节数量的限制,UCS-2只能表示最多65536个字符。Unicode的4字节形式被称为UCS-4或UTF-32,能够定义Unicode的全部扩展,最多可定义100万个以上唯一字符

简单来说每个国家能用多位二进制数(必须是8的倍数)来表示一个符号,并且所有国家之间不能出现重复的情况,也就是对于同样的二进制, 不能有两个含义

unicode字符集的问题(不能没有编码)

需要注意的是,Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。

比如,汉字严的 Unicode 是十六进制数4E25,转换成二进制数足足有15位(100111000100101),也就是说,这个符号的表示至少需要2个字节。表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多。

这里就有两个严重的问题,第一个问题是,如何才能区别 Unicode 和 ASCII ?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果 Unicode 统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。

它们造成的结果是:1)出现了 Unicode 的多种存储方式,也就是说有许多种不同的二进制格式,可以用来表示 Unicode。2)Unicode 在很长一段时间内无法推广,直到互联网的出现。

UTF-8(只是编码)

互联网的普及,强烈要求出现一种统一的编码方式。UTF-8 就是在互联网上使用最广的一种 Unicode 的实现方式。其他实现方式还包括 UTF-16(字符用两个字节或四个字节表示)和 UTF-32(字符用四个字节表示),不过在互联网上基本不用。重复一遍,这里的关系是,UTF-8 是 Unicode 的实现方式之一。

UTF-8 最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。

UTF-8 的编码规则很简单,只有二条:

1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。

2)对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。(编码后的那一串二进制数和unicode字符集本身的这一串二进制数就产生了不同)

规则二的前提肯定是编码前知道多少个字节属于一个字符

实例讲解多字节如何进行UTF-8编码

如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。

下面,还是以汉字严为例,演示如何实现 UTF-8 编码。

严的 Unicode 是4E25(100111000100101),可以发现4E25处在第三行的范围内(0000 0800 - 0000 FFFF),因此严的 UTF-8 编码需要三个字节,即格式是1110xxxx 10xxxxxx 10xxxxxx。然后,从严的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,严的 UTF-8 编码是11100100 10111000 10100101,转换成十六进制就是E4B8A5。