Unicode、UTF-8、UTF-16、UTF-32的区别?

201 阅读7分钟

1)Unicode

在说Unicode​之前需要先了解一下ASCII​码:ASCII 码(American Standard Code for Information Interchange​)称为美国标准信息交换码。

  • 它是基于拉丁字母的一套电脑编码系统。
  • 它定义了一个用于代表常见字符的字典。
  • 它包含了"A-Z"(包含大小写),数据"0-9" 以及一些常见的符号。
  • 它是专门为英语而设计的,有128个编码,对其他语言无能为力

​ASCII​码可以表示的编码有限,要想表示其他语言的编码,还是要使用Unicode​来表示,可以说Unicode​是ASCII​ 的超集。

​Unicode​全称 Unicode Translation Format​,又叫做统一码、万国码、单一码。Unicode​ 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。

​Unicode​的实现方式(也就是编码方式)有很多种,常见的是UTF-8、UTF-16、UTF-32和USC-2。

(2)UTF-8

​UTF-8​是使用最广泛的Unicode​编码方式,它是一种可变长的编码方式,可以是1—4个字节不等,它可以完全兼容ASCII​码的128个字符。

注意: UTF-8​ 是一种编码方式,Unicode​是一个字符集合。

​UTF-8​的编码规则:

  • 对于单字节的符号,字节的第一位为0,后面的7位为这个字符的Unicode​编码,因此对于英文字母,它的Unicode​编码和ACSII​编码一样。
  • 对于n字节的符号,第一个字节的前n位都是1,第n+1位设为0,后面字节的前两位一律设为10,剩下的没有提及的二进制位,全部为这个符号的Unicode​码 。

来看一下具体的Unicode​编号范围与对应的UTF-8​二进制格式 :

编码范围(编号对应的十进制数)二进制格式
0x00—0x7F (0-127)0xxxxxxx
0x80—0x7FF (128-2047)110xxxxx 10xxxxxx
0x800—0xFFFF (2048-65535)1110xxxx 10xxxxxx 10xxxxxx
0x10000—0x10FFFF (65536以上)11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

那该如何通过具体的Unicode​编码,进行具体的UTF-8​编码呢?步骤如下:

  • 找到该Unicode​编码的所在的编号范围,进而找到与之对应的二进制格式
  • 将Unicode​编码转换为二进制数(去掉最高位的0)
  • 将二进制数从右往左一次填入二进制格式的X​中,如果有X​未填,就设为0

来看一个实际的例子: “马” 字的Unicode​编码是:0x9A6C​,整数编号是39532​(1)首选确定了该字符在第三个范围内,它的格式是 1110xxxx 10xxxxxx 10xxxxxx​(2)39532对应的二进制数为1001 1010 0110 1100​(3)将二进制数填入X中,结果是:11101001 10101001 10101100​

(3)UTF-16

  1. 平面的概念

在了解UTF-16​之前,先看一下平面的概念:Unicode​编码中有很多很多的字符,它并不是一次性定义的,而是分区进行定义的,每个区存放65536(216)个字符,这称为一个平面,目前总共有17 个平面。

最前面的一个平面称为基本平面,它的码点从0 — 216-1,写成16进制就是U+0000 — U+FFFF​,那剩下的16个平面就是辅助平面,码点范围是 U+10000—U+10FFFF​。

  1. UTF-16 概念:

​UTF-16​也是Unicode​编码集的一种编码形式,把Unicode​字符集的抽象码位映射为16位长的整数(即码元)的序列,用于数据存储或传递。Unicode​字符的码位需要1个或者2个16位长的码元来表示,因此UTF-16​也是用变长字节表示的。

  1. UTF-16 编码规则:
  • 编号在 U+0000—U+FFFF​ 的字符(常用字符集),直接用两个字节表示。
  • 编号在 U+10000—U+10FFFF​ 之间的字符,需要用四个字节表示。
  1. 编码识别

那么问题来了,当遇到两个字节时,怎么知道是把它当做一个字符还是和后面的两个字节一起当做一个字符呢?

​UTF-16​ 编码肯定也考虑到了这个问题,在基本平面内,从 U+D800 — U+DFFF​ 是一个空段,也就是说这个区间的码点不对应任何的字符,因此这些空段就可以用来映射辅助平面的字符。

辅助平面共有 220 个字符位,因此表示这些字符至少需要 20 个二进制位。UTF-16​ 将这 20 个二进制位分成两半,前 10 位映射在 U+D800 — U+DBFF​,称为高位(H),后 10 位映射在 U+DC00 — U+DFFF​,称为低位(L)。这就相当于,将一个辅助平面的字符拆成了两个基本平面的字符来表示。

因此,当遇到两个字节时,发现它的码点在 U+D800 —U+DBFF​之间,就可以知道,它后面的两个字节的码点应该在 U+DC00 — U+DFFF​ 之间,这四个字节必须放在一起进行解读。

  1. 举例说明

以 "𡠀" 字为例,它的 Unicode​ 码点为 0x21800​,该码点超出了基本平面的范围,因此需要用四个字节来表示,步骤如下:

  • 首先计算超出部分的结果:0x21800 - 0x10000​
  • 将上面的计算结果转为20位的二进制数,不足20位就在前面补0,结果为:0001000110 0000000000​
  • 将得到的两个10位二进制数分别对应到两个区间中
  • ​U+D800​ 对应的二进制数为 1101100000000000​, 将0001000110​填充在它的后10 个二进制位,得到 1101100001000110​,转成 16 进制数为 0xD846​。同理,低位为 0xDC00​,所以这个字的UTF-16​ 编码为 0xD846 0xDC00​

(4) UTF-32

​UTF-32​ 就是字符所对应编号的整数二进制形式,每个字符占四个字节,这个是直接进行转换的。该编码方式占用的储存空间较多,所以使用较少。

比如“马” 字的Unicode编号是:U+9A6C​,整数编号是39532​,直接转化为二进制:1001 1010 0110 1100​,这就是它的UTF-32编码。

(5)总结

Unicode、UTF-8、UTF-16、UTF-32有什么区别?

  • ​Unicode​ 是编码字符集(字符集),而UTF-8​、UTF-16​、UTF-32​是字符集编码(编码规则);
  • ​UTF-16​ 使用变长码元序列的编码方式,相较于定长码元序列的UTF-32​算法更复杂,甚至比同样是变长码元序列的UTF-8​也更为复杂,因为其引入了独特的代理对这样的代理机制;
  • ​UTF-8​需要判断每个字节中的开头标志信息,所以如果某个字节在传送过程中出错了,就会导致后面的字节也会解析出错;而UTF-16​不会判断开头标志,即使错也只会错一个字符,所以容错能力教强;
  • 如果字符内容全部英文或英文与其他文字混合,但英文占绝大部分,那么用UTF-8​就比UTF-16​节省了很多空间;而如果字符内容全部是中文这样类似的字符或者混合字符中中文占绝大多数,那么UTF-16​就占优势了,可以节省很多空间;