目录
Unicode、UTF-8、UTF-16、UTF-32有什么区别?
这是之前在阿里淘系前端面经上看到的一道题,自己也不是很了解,查阅了相关资料,来整理一下。
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的实现的方式:
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
。
2. UTF-16 概念:
UTF-16
也是Unicode
编码集的一种编码形式,把Unicode
字符集的抽象码位映射为16位长的整数(即码元)的序列,用于数据存储或传递。Unicode
字符的码位需要1个或者2个16位长的码元来表示,因此UTF-16
也是用变长字节表示的。
3. UTF-16 编码规则:
- 编号在
U+0000—U+FFFF
的字符(常用字符集),直接用两个字节表示。 - 编号在
U+10000—U+10FFFF
之间的字符,需要用四个字节表示。
4. 编码识别
那么问题来了,当我们遇到两个字节时,我们怎么知道是把它当做一个字符还是和后面的两个字节一起当做一个字符呢?
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
之间,这四个字节必须放在一起进行解读。
5. 举例说明
以 “𡠀” 字为例,它的 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
就占优势了,可以节省很多空间
其实最主要的还是要理解这几个名词的概念,以及编码的步骤和原理。
最后的最后
看了很多篇网上的文章,也查了很多资料,有些地方觉得比较好的就搬过来了,自己也有总结整理。总之,通过写这篇文章的过程,对Unicode
、UTF-8
、UTF-16
、UTF-32
都有了全新的认识,如果有不准确的地方,欢迎指正鸭~