最近在开发上遇到了点编码的问题,又重新学习了字符集以及编码的知识。特此记录一下。
核心
我比较喜欢一开始讲述核心的东西,这样子读者可以带着这些知识往下看,并且在翻阅到这个页面的时候可以一眼就可以看到最关键的内容。
- Unicode是字符集
- UTF-8是编码规则
Unicode
在很久很久之前,几个美国人发现用8个可以开合的晶体管来表示不同的状态,即可以表示2^8=256种状态,并且他们把这个8个开关称为一个字节。然后这几个人美国人又规定,把编码(十进制值)从0到32的值所表示的状态分别规定了特殊的用途,终端、打印机等输出设备一收到这些值,就会做出响应的操作。
比如:
输出设备遇到0x10,终端就换行。
输出设备遇到0x07,终端就发出嘟嘟的声响。(很多人可能不知道命令行其实是可以发出警报声的)
输出设备遇到0x1b,打印机就打印反白的字符,或者终端用彩色表示字母。、
输出设备遇到...
好了,总之这就是这个几个美国人设计的规范。这几个美国人感觉这样子做很不错,并且把0x20以下的字节码状态称为控制码
。此时这几个美国人又把所有的空格、标点符号、数字、大小写字母分别用连续的字节状态来表示,一直定义到了127。这样子当着几个人每个人想要输出字符串到输出设备时候,只要输入对应的二进制的状态码即可。其实这也是最早的计算机的原型。很快,这几个美国人把这个编码向全国推广,希望可以统一字符的标准。果不其然,这种字符集标准很快在美国地区得到普及,并且有了一个新的名称--ASCII(American Standard Code for Information Interchange,美国信息互换标准代码)
编码。
后来,随着计算机的普及。世界各地的人都开始用计算机。就以我们国家做例子。但是这个字符集的短板也就出现了,当时中国人发现计算机无法输入中文!原因就在于那几个美国人只定义了英文的英语字符(a,b,c,d...等)。根本没有考虑其他国家的语言输入。
于是我们勤劳的中国人打算自己定义一套适用于中文的字符集。我们在ASCII
字符集的基础上,把127号的以后的编码都直接去掉了,规定:一个小于127的字符的与原来的意义相同,但是当两个大于127的字符连在一起,就表示一个汉字,前面的一个字节(称为高字节)从0xA1用到0xF7,后面一个字节(低字节)从0xA1到0xFE,这样子我们就能够组合处于大于7000多个汉子了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的”全角”字符,而原来在127号以下的那些就叫”半角”字符了。中国人民看到这样很不错,于是就把这种汉字方案叫做 “GB2312“。GB2312 是对 ASCII 的中文扩展。
欲望推动着科技不断进步。后来随着各种字符文字的增加,又在GB2312的基础上又扩展出了GBK编码方案、GBK又扩展出了GB18030编码方案。
可以看到,ASCII是一个字节表示一个字符,而为了支持中文汉字的编码方案,我们出现了两个字符表示一个汉字。但是在计算机如何识别它们呢?答案是计算机是按照ASCII的标准进行读取的,所以在当时的编码方案下,一个中文是算两个英文字符的。
在伟大的中国智慧人民在创新的同时,在地球上其他国家其他地区的人也在创建适用于它们文字的编码方案。
于是问题就出来了。两个编码方案不一样的计算机无法实现正常的交流。
为了解决这个问题。一个叫做ISO(国际标准化组织)
的组织出现了,它们想解决这个问题。他们采用的解决方案也很简单:废除了所有的地区性的编码方案,自己重新搞了一个包括了地球上所有文字、所有字符和符号的编码。然后推广并让大家都使用它。这就是Unicode
。
Unicode
规定:全部字符都必须用两个以及两个以上字节来定义,也就是必须16位以及16位以上来统一所有的字符,对于ASCII里的127号以及以下的字符编码保持不变,只是将其长度从8位扩展至16位,高位补0。这样做的问题也很明显,就是当文本中的所有的字符都是英文时,会浪费一倍的储存空间。在Unicode
中,无论这个文字是由多少个字节组成的,都是算位一个字符。
Unicode
存在以下几个缺点
- 当读取一个文本文件时候的,计算机会无法正常读取文本内容。
因为在unicode中的字节都是可变的,从2个字节到N个字节不等,当某个文本中包含同时包含有两个和三个字节的字符时候,计算机很有可能将三个字节的文本拆开来读取。我这里有一段内容为你好简书
,假设这四个字在Unicode占用的字节数分别为:2个字节、3个字节、3个字节。2个字节。那么计算机在读取这段文字的时候,就很容易读成22222字节的方式来读出五个字符,当然,读取出来的内容和原来的你好简书
是完全不一样的。 - 当文本文字中有英文字符的时,必然会造成存储空间的浪费。
就像上面我们所说的,unicode中所有的字符都至少为两个字节,这就出现了问题。一个ASCII的英文字只需要占用一个字节就可表示,但是在unicode中要用两个字节。所以当文本字符内容中英文字符比较多的情况下,存储空间、以及网络传输带宽(文件传输的时候)浪费就特别明显。
由于这些原因,Unicode
在很长的一段时间内无法推广,直到互联网的出现,为了解决Unicode
如何在网络傻姑娘传输的问题,各种各类的UTF(UCS Transfer Format)
的标准诞生了。
UTF
UTF
是个统称,它包括了UTF-8
、UTF-16
等传输标准。不同的地方在于,UTF-8
每次传输8个位的数据,而后者每次传输的数据大小为16位。
UTF-8
目前是互联上使用最广的一种unicode的实现方式,这是为传输而设计的编码,并使编码无国家。UTF-8
最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节来表示一个字符,根据不同的符号而变化所要存储的字节长度。当字符在ASCII码的范围内,就用一个字节来表示,当unicode中一个汉字表示两个字节时,在UTF-8中用三个字节来表示。
那怎么将unicode转换为utf-8编码呢?
Unicode符号范围 | UTF-8编码方式
(十六进制) | (二进制)
----------------------+---------------------------------------------
0 <--> 0x7f | 0xxxxxxx
0x80 <--> 0x7FF | 110xxxxx 10xxxxxx
0x800 <--> 0xFFFF | 1110xxxx 10xxxxxx 10xxxxxx
0x10000 <--> 0x10FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
····
····
这就是unicode
转为utf-8的算法规则。这算法规则也很好理解,以第一行算法为例子,二进制最大的值为01111111
,换算成十六进制就是0x7f
。第二行、第三行..都以此类推。
这里来举几个例子。
简
字的unicode编码为\u7b80
,换算成二进制为0111 1011 1000 0000
。从算法规则中我们可以看到,简字在utf-8编码中,需要三个字节表示(0x7b80在0x800与0xFFFF之间)。然后将简字的二进制值从低位区向高位区填充,高位没值补0。那么简字在utf-8编码中的表示为11100111 10101110 10000000
,即0xe7ae80
。
书
字的unicode编码为\u4e66
,换算成二进制为100 1110 0110 0110
。从算法规则中我们可以得知,书字需要三个字节来表示,得到结果为11100100 10111001 10100110
,即0xe4b9a6
。
A
字的unicode的编码为\u0041
,换算成二进制为0100 0001
。在utf-8
中,得到的结果为0100 0001
。
UTF-8编码标准很好地解决了我们上面所说的unicode的所面临的问题。
utf-8解决了计算机无法正确读取unicode编码的问题
如果读者仔细观察utf-8的转换规则你会发现一个规律,utf-8编码的内容首个字节都是以0、110、1110、11110打头,后面接上几个10开头的字节(单字节时候不接)。
这里以读取某段内容为例,计算机是这么读取每个字符的:
当读取到某个字节以0开头,那么计算机就知道这是个单字节字符,那么只会读取一个字节。
当读取到某个字节以110开头的,那么计算机除了读取这个110开头的字节外,还会读取紧跟的后面的一个字节。
当读取到某个字节以1110开头的,那么计算机除了读取这个1110开头的字节外,还会读取紧跟后面的两个字节。
当读取到某个字节以11110开头的,那么计算机除了读取这个11110开头的字节,还会读取紧跟后面的三个字节。
....
将该字符所需要的字节读取出来后,转换为unicode值(注意这里和上面的转换方式是相反的),最后从unicode值的映射中获取到这个值所对应的字符,最后将这个字符在屏幕上显示出来。
在这种编码下,计算机能正确读取每个字符的字节个数。从而获取获取到正确的字符。
utf-8解决了存储空间和网络带宽资源浪费的问题
无论是存储空间和网络带宽的资源浪费,都是因为在unicode中,为英文字符定义了多余的字节空间造成的。而utf-8很巧妙地解决了这一问题。从utf-8编码的转换规则中我们可以看到,一个英文字符在unicode中占用两个字节,而在utf-8中只占用了一个字节。但是其实!在utf-8中汉字的字节数反而增加了,比如我们上文说的简字,在unicode中占用了两个字节,而在utf-8中要用三个字节来表示。不过这并不是资源的浪费,而是utf-8编码方案为了解决上面所说的那个问题而规定的标准。