【底层原理】Ascii 、Unicode与UTF-8的区别

web开发的过程中,动不动就遇到编码不一致的问题,然后转换的过程中,一会Ascii 一会unicode 一会 UTF8,这到底是什么关系呢?百度了一堆资料,彻底清楚来龙去脉了,简单做个记录,方便自己,方便后来人。

按照起始顺序: 依次是Ascii -> unicode -> UTF32 -> UTF16 -> UTF8


ASCII

ASCII (American Standard Code for Information Interchange) 翻译过来是"美国信息交换标准代码", 就知道是老美设立的标准了;ASCII第一次以规范标准的类型发表是在1967年,最后一次更新则是在1986年,到目前为止共定义了128个字符;老美的逻辑非常简单,加起来就那点英文字母,在加上一些标点符号,128个足足够用了,都不够一个字节的(一个字节8位,可表示256个字符)。

所以早期人们用8位二进制来编码英文字母(最前面的一位是0),也就是说,将英文字母和一些常用的字符和这128中二进制0、1串一一对应起来,比如说 大写字母“A”所对应的二进制位“01000001”,转换为十六进制为41。

在美国,这128是够了,但是其他国家不答应啊,他们的字符和英文是有出入的,比如在法语中在字母上有注音符号,如 é ,这个怎么表示成二进制?

所以各个国家就决定把字节中最前面未使用的那一个位拿来使用,原来的128种状态就变成了256种状态,比如é就被编码成130(二进制的10000010)。

为了保持与ASCII码的兼容性,一般最高为为0时和原来的ASCII码相同,最高位为1的时候,各个国家自己给后面的位(1xxx xxxx)赋予他们国家的字符意义。

但是这样一来又有问题出现了,不同国家对新增的128个数字赋予了不同的含义,比如说130在法语中代表了é,但是在希伯来语中却代表了字母Gimel,所以这就成了不同国家有不同国家的编码方式,所以如果给你一串二进制数,你想要解码,就必须知道它的编码方式,不然就会出现我们有时候看到的乱码 。


Unicode

Unicode为世界上所有字符都分配了一个唯一的数字编号,这个编号范围从 0x000000 到 0x10FFFF(十六进制),有110多万,每个字符都有一个唯一的Unicode编号,这个编号一般写成16进制,在前面加上U+。例如:“马”的Unicode是U+9A6C。

Unicode就相当于一张表,建立了字符与编号之间的联系,它是一种规定,Unicode本身只规定了每个字符的数字编号是多少,并没有规定这个编号如何存储。

有的人会说了,那我可以直接把Unicode编号直接转换成二进制进行存储,是的,你可以,但是这个就需要人为的规定了,而Unicode并没有说这样弄,因为除了你这种直接转换成二进制的方案外,还有其他方案,接下来我们会逐一看到。 

编号怎么对应到二进制表示呢?有多种方案:主要有UTF-8,UTF-16,UTF-32。


UTF32 

UTF= UCS Transformation Format UCS转换格式,其中UCS就是unicode

UTF-32用四个字节表示,处理单元为四个字节,比如我们一次要处理四个字节 12 34 56 78,这四个字节是表示0x12 34 56 78;


UTF16

UTF-16使用变长字节表示

① 对于编号在U+0000到U+FFFF的字符(常用字符集),直接用两个字节表示。 
② 编号在 U+10000到U+10FFFF之间的字符,需要用四个字节表示。


UTF8

UTF-8就是使用变长字节表示,顾名思义,就是使用的字节数可变,这个变化是根据Unicode编号的大小有关,编号小的使用的字节就少,编号大的使用的字节就多。使用的字节个数从1到4个不等。

UTF-8的编码规则是:

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

② 对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10,剩下的没有提及的二进制位,全部为这个符号的Unicode码 。

举个例子:比如说一个字符的Unicode编码是130,显然按照UTF-8的规则一个字节是表示不了它(因为如果是一个字节的话前面的一位必须是0,最大能表示到127, 130表示不出来),所以需要两个字节(n = 2)。

根据规则,第一个字节的前 2 位都设为1,第3(2+1)位设为0,则第一个字节为:110X XXXX,后面字节的前两位一律设为10,后面只剩下一个字节,所以后面的字节为:10XX XXXX。

所以它的格式为110XXXXX 10XXXXXX

那么对于一个具体的Unicode编号,具体怎么进行UTF-8的编码呢?

首先找到该Unicode编号所在的编号范围,进而可以找到与之对应的二进制格式,然后将该Unicode编号转化为二进制数(去掉高位的0),最后将该二进制数从右向左依次填入二进制格式的X中,如果还有X未填,则设为0 。

比如:“马”的Unicode编号是:0x9A6C,整数编号是39532,对应第三个范围(2048 - 65535),其格式为:1110XXXX 10XXXXXX 10XXXXXX,39532 对应的二进制是 1001 1010 0110 1100,将二进制填入进入就为: 

11101001 10101001 10101100

(汉字集中落在这个区间,所以汉字的UTF8占3个字节大小)


总结:

ASCII是老美创立的存储字符的标准,但是老美只考虑了自己使用的阿拉丁符号,因此1个字节就可以存的下,但是像中文这些就不可能实现用ASCII编码,或者得用多个ASCII码表示一个汉字,因此有了unicode编码,用多个字节表示目前全世界的字符,unicode给全世界的字符都赋予了值,但是这些值具体用几个字节存储呢?如何存储呢?这就有了UTF8 UTF16 UTF32的存在,他们是针对unicode的实现。


————————————————
原文链接:https://blog.csdn.net/slhlde/java/article/details/82251776

猜你喜欢

转载自www.cnblogs.com/liuyong18/p/13207706.html