Unicode、UTF-8、UTF-16终于懂了
关注
“脚本之家
”,与百万开发者在⼀起
unicode汉字
来源 | Linux开发那些事⼉(ID:LinuxThings)
如若转载请联系原
计算机起源于美国,上个世纪,他们对英语字符与⼆进制位之间的关系做了统⼀规定,并制定了⼀套字符编码规则,这套编码规则被称为ASCII编码
ASCII 编码⼀共定义了128个字符的编码规则,⽤七位⼆进制表⽰ ( 0x00 - 0x7F ), 这些字符组成的集合就叫做 ASCII 字符集
随着计算机的普及,在不同的地区和国家⼜出现了很多字符编码,⽐如: ⼤陆的 GB2312、港台的 BIG5, ⽇本的 Shift JIS等等
由于字符编码不同,计算机在不同国家之间的交流变得很困难,经常会出现乱码的问题,⽐如:对于同⼀个⼆进制数据,不同的编码会解析出不同的字符
当互联⽹迅猛发展,地域限制打破之后,⼈们迫切的希望有⼀种统⼀的规则, 对所有国家和地区的字符进⾏编码,于是Unicode 就出现了
Unicode 简介
Unicode 是国际标准字符集,它将世界各种语⾔的每个字符定义⼀个唯⼀的编码,以满⾜跨语⾔、跨平台的⽂本信息转换
Unicode 字符集的编码范围是 0x0000 - 0x10FFFF, 可以容纳⼀百多万个字符,每个字符都有⼀个独⼀⽆⼆的编码,也即每个字符都有⼀个⼆进制数值和它对应,这⾥的⼆进制数值也叫码点, ⽐如:汉字 "中"的码点是 0x4E2D, ⼤写字母A的码点是 0x41, 具体字符对应的 Unicode 编码可以查询 Unicode字符编码表
字符集和字符编码
字符集是很多个字符的集合,例如 GB2312 是简体中⽂的字符集,它收录了六千多个常⽤的简体汉字及⼀些符号,数字,拼⾳等字符
字符编码是字符集的⼀种实现⽅式,把字符集中的字符映射为特定的字节或字节序列,它是⼀种规则
⽐如:Unicode 只是字符集,UTF-8、UTF-16、UTF-32 才是真正的字符编码规则
Unicode 字符存储
Unicode 是⼀个符号集,它只规定了每个符号的⼆进制值,但是符号具体如何存储它并没有规定
前⾯提到, Unicode 字符集的编码范围是 0x0000 - 0x10FFFF,因此需要 1 到 3 个字节来表⽰
那么,对于三个字节的 Unicode字符,计算机怎么知道它表⽰的是⼀个字符⽽不是三个字符呢?
如果所有字符都⽤三个字节表⽰,那么对于那些⼀个字节就能表⽰的字符来说,有两个字节是⽆意义的,对于存储来说,这是极⼤的浪费,假如 , ⼀个普通的⽂本, ⼤部分字符都只需⼀个字节就能表⽰,现在如果需要三个字节才能表⽰,⽂本的⼤⼩会⼤出三倍左右
因此,Unicode 出现了多种存储⽅式,常见的有 UTF-8、UTF-16、UTF-32,它们分别⽤不同的⼆进制格式来表⽰Unicode 字符
UTF-8、UTF-16、UTF-32 中的 "UTF" 是 "Unicode Transformation Format" 的缩写,意思是"Unicode 转换格式",后⾯的数字表明⾄少使⽤多少个⽐特位来存储字符, ⽐如:UTF-8 最少需要8个⽐特位也就是⼀个字节来存储,对应的,UTF-16 和 UTF-32 分别需要最少 2 个字节和 4 个字节来存储
UTF-8 编码
UTF-8: 是⼀种变长字符编码,被定义为将码点编码为 1 ⾄ 4 个字节,具体取决于码点数值中有效⼆进制位的数量UTF-8 的编码规则:
1.对于单字节的符号,字节的第⼀位设为 0 ,后⾯ 7 位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和
ASCII 码是相同的, 所以 UTF-8 能兼容 ASCII 编码,这也是互联⽹普遍采⽤ UTF-8 的原因之⼀
1.对于 n 字节的符号( n > 1 ),第⼀个字节的前 n 位都设为 1 ,第 n + 1 位设为 0 ,后⾯字节的前两位⼀律设为 10
。剩下的没有提及的⼆进制位,全部为这个符号的 Unicode 码
下表是Unicode编码对应UTF-8需要的字节数量以及编码格式
Unicode编码范围(16进制)UTF-8编码⽅式(⼆进制)
000000 - 00007F0xxxxxxx ASCII码
000080 - 0007FF110xxxxx 10xxxxxx
000800 - 00FFFF1110xxxx 10xxxxxx 10xxxxxx
01 0000 - 10 FFFF11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
表格中第⼀列是Unicode编码的范围,第⼆列是对应UTF-8编码⽅式,其中红⾊的⼆进制 "1"和 "0"是固定的前缀, 字母 x 表⽰可⽤编码的⼆进制位
根据上⾯表格,要解析 UTF-8 编码就很简单了,如果⼀个字节第⼀位是 0,则这个字节就是⼀个单独的字符,如果第⼀位是 1,则连续有多少个 1,就表⽰当前字符占⽤多少个字节
位是 1,则连续有多少个 1,就表⽰当前字符占⽤多少个字节
下⾯以 "中"字为例来说明 UTF-8 的编码,具体的步骤如下图,为了便于说明,图中左边加了 1,2,3,4 的步骤编号
⾸先查询 "中"字的 Unicode 码 0x4E2D, 转成⼆进制, 总共有 16 个⼆进制位,具体如上图步骤1 所⽰
通过前⾯的 Unicode 编码和 UTF-8 编码的表格知道,Unicode 码 0x4E2D对应 000800 - 00FFFF的范围,所以, "中"字的 UTF-8 编码需要 3个字节,即格式是 1110xxxx 10xxxxxx 10xxxxxx
然后从 "中"字的最后⼀个⼆进制位开始,按照从后向前的顺序依次填⼊格式中的 x字符,多出的⼆进制补为 0,具体如上图步骤2、步骤3 所⽰
UTF-16 编码
UTF-16 也是⼀种变长字符编码, 这种编码⽅式⽐较特殊, 它将字符编码成 2 字节或者 4 字节
具体的编码规则如下:
1.对于 Unicode 码⼩于 0x10000 的字符,使⽤ 2 个字节存储,并且是直接存储 Unicode 码,不⽤进⾏编码转换1.
1.⼤于 0x10FFFF 的 Unicode 码⽆法⽤ UTF-16 编码
下表是Unicode编码对应UTF-16编码格式
Unicode编码范围(16进制)具体Unicode码(⼆进制)UTF-16编码⽅式(⼆进制)字节
0000 0000 - 0000 FFFF xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx2
0001 0000 - 0010 FFFF yy yyyyyyyy xx xxxxxxxx110110yy yyyyyyyy 110111xx xxxxxxxx4
表格中第⼀列是Unicode编码的范围,第⼆列是具体Unicode码的⼆进制 ( 第⼆⾏的第⼆列表⽰的是 Unicode 码减去0x10000后的⼆进制 ) , 第三列是对应UTF-16编码⽅式,其中红⾊的⼆进制 "1"和 "0"是固定的前缀, 字母 x和 y表⽰可⽤编码的⼆进制位,第四列表⽰编码占⽤的字节数
前⾯提到过, "中"字的 Unicode 码是 4E2D, 它⼩于 0x10000,根据表格可知,它的 UTF-16 编码占两个字节,并且和Unicode 码相同,所以 "中"字的 UTF-16 编码为 4E2D
下⾯以这个⽼的南阿拉伯字母的 Unicode 码 0x10A6F为例来说明 UTF-16 4字节的编码,具体步骤如下,为了便于说明,图中左边加了 1,2,3,4 、5 的步骤编号
⾸先把 Unicode 码 0x10A6F转成⼆进制, 对应上图的步骤 1
UTF-32 编码
UTF-32 是固定长度的编码,始终占⽤ 4 个字节,⾜以容纳所有的 Unicode 字符,所以直接存储 Unicode 码即可,不需要任何编码转换。虽然浪费了空间,但提⾼了效率。
UTF-8、UTF-16、UTF-32 之间如何转换
前⾯介绍过,UTF-8、UTF-16、UTF-32 是 Unicode 码表⽰成不同的⼆进制格式的编码规则,同样,通过这三种编码的⼆进制表⽰,也能获得对应的 Unicode 码,有了字符的 Unicode 码,按照上⾯介绍的 UTF-8、UTF-16、UTF-32 的编码⽅法就能转换成任⼀种编码了
UTF 字节序
最⼩编码单元是多字节才会有字节序的问题存在,UTF-8 最⼩编码单元是⼀字节,所以它是没有字节序的问题,UTF-16 最⼩编码单元是 2 个字节,在解析⼀个 UTF-16 字符之前,需要知道每个编码单元的字节序
所以,对于多字节的编码单元,需要有⼀个标记显式的告诉计算机,按照什么样的顺序解析字符,也就是字节序,字节序分为⼤端字节序和⼩端字节序
⼩端字节序简写为 LE( Little-Endian ), 表⽰低位字节在前,⾼位字节在后, ⾼位字节保存在内存的⾼地址端,⽽低位字节保存在内存的低地址端
⼤端字节序简写为 BE( Big-Endian ), 表⽰⾼位字节在前,低位字节在后,⾼位字节保存在内存的低地址端,低位字节保存在在内存的⾼地址端
下⾯以 0x4E2D为例来说明⼤端和⼩端,具体参见下图:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。