一文读懂Unicode,UTF-8,UTF-16/32,BOM

对英语字符与二进制位之间的关系做了统一规定,并制定了一套字符编码规则,这套编码规则被称为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 是一个符号集,它只规定了每个符号的二进制值,但是符号具体如何存储它并没有规定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 个字节,具体取决于码点数值中有效二进制位的数量。对于单字节的符号,字节的第一位设为 0,后面 7 位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的,能兼容 ASCII 编码,需一个字节对于中文,中文字符需要两字节去表示,然而如果只用两个字节,无法储存前三位110,因此中文要使用三个字节。三个字节下,一共是24位,第一个字节头四位是:1110,后两个字节的前两位都是:10,那么24位-8位=16位,刚好两个字节去表示Unicode下的任意一个非ANSI字符。然,中国的汉字多达10多万,如果用3个字节来表示,一共只有2^16(65535)种可能,不足以表示10多万的汉字。所以中日韩的超大字符集是采用的4个字节来表示的,但是平时使用超大字符集的概率0.01%都不到。所以我们一般认为日常的中文在UTF-8中占三个字节。如果多个字节提供的位数超过了所需要的,多余的位以0补全到编码前面。

 

对于n字节的符号(n > 1),第一个字节的前n位都设为1,第 n + 1 位设为 0,后面字节的前两位一律设为10 。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。要解析 UTF-8 编码首先看第一个字节。如果一个字节第一位是0,则这个字节就是一个单独的字符,如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。

UTF-8优势:

  1. 兼容 ASCII
  2. 能适应许多 C 库中的 \0 结尾惯例
  3. 没有大小端字节序问题,信息交换便捷
  4. 良好的多语种支持(只要是UTF-8编码,不需要额外下载语言支持包)
  5. 以英文和西文符号比较多的场景下(例如 HTML/XML),编码较短
  6. 由于是变长,字符空间足够大,即使Unicode 新标准收录更多字符,UTF-8 也能兼容
  7. 不存在大小端字节序问题,信息交换时非常便捷
  8. 容错性高,局部的字节错误(丢失、增加、改变)不会导致连锁性的错误,因为 UTF-8 的字符边界很容易检测出来。
  9. 不需要BOM(但是MSVC在解析不带BOM的C++代码文件中的常量字符串的时候会解析出错)

 UTF-8缺点:

  1. 对英语国家无额外储存负担,但对中日韩储存冗余,一个字符占用三个字节,储存和传输效率过低。
  2. 变长导致效率低下,无论是计算字节数还是执行索引,操作效率都不高。

UTF-16 编码

UTF-16 也是一种变长字符编码, 这种编码方式比较特殊, 它将字符编码成 2 字节 或者 4 字节

具体的编码规则如下:

对于 Unicode 码小于 0x10000 的字符, 使用 2 个字节存储,并且是直接存储 Unicode 码,不用进行编码转换;对于 Unicode 码在 0x10000 和 0x10FFFF 之间的字符,使用 4 个字节存储,这 4 个字节分成前后两部分,每个部分各两个字节,其中,前面两个字节的前 6 位二进制固定为 110110,后面两个字节的前 6 位二进制固定为 110111, 前后部分各剩余 10 位二进制表示符号的 Unicode 码 减去 0x10000 的结果;大于 0x10FFFF 的 Unicode 码无法用 UTF-16 编码。例子:"中"字的Unicode码是4E2D, 它小于0x10000,根据表格可知,它的 UTF-16 编码占两个字节,并且和 Unicode 码相同,所以"中"字的 UTF-16 编码为4E2D。

UTF-16特点

  1. 最流行的操作系统和 UI framework 的内部字符串表达都是 UTF-16

Windows API 的 Wide Char 表达是 UTF-16: Unicode (Windows), L"" 表示是转换为 wide char。

Cocoa 的 NSString 和 Core Foundation 的 CFString 内部表达都是 UTF-16,所以其实 OS X 和 iOS 内部处理都用的是 UTF-16。

Java String 的内部表达是 UTF-16,所以大量跨平台程序和 Android 程序其实内部也在用 UTF-16。

  1. 由于其是双字节的,在计算字符串长度、执行索引操作时速度很快。

注:但是UTF-16也是变长的,Unicode扩展到9万多以后,也要通过变长来支持了,否则将无法储存。

  1. 由于使用两个字节为单位,需要区分大小端。
  2. 容错性低。局部的字节错误,特别是丢失或增加可能导致后续所有字符全部错乱。

UTF-32 编码

UTF-32 是固定长度的编码,始终占用 4 个字节,读到内存中是均匀的整型数组,可以方便地随机访问任何一个字符,且足以容纳所有的 Unicode 字符,所以直接存储 Unicode 码即可,不需要任何编码转换。虽然浪费了空间,但提高了效率。

应用场景:

在磁盘上或进行网络交换时,多采用UTF-8,由于其灵活性,在互联网通信中被编码影响小,兼容性强。在程序内部处理时多转换为UTF-16/32。这样既可以保证信息交换时容易实现相互兼容,在内部处理时也比较简单,对非I/O密集型性能也相对良好。

大小端/字节序

在解析一个 UTF-16 字符之前,需要知道每个编码单元的字节序比如:前面提到过,"中" 字的 Unicode 码是 4E2D, "ⵎ" 字符的 Unicode 码是 2D4E, 当我们收到一个 UTF-16 字节流 4E2D 时,计算机如何识别它表示的是字符 "中" 还是 字符 "ⵎ" 呢所以,对于多字节的编码单元,需要有一个标记显式标记解析字符的顺序,也就是字节序,字节序分为大端字节序和小端字节序

小端字节序简写为 LE( Little-Endian ), 表示低位字节在前,高位字节在后, 高位字节保存在内存的高地址端,而低位字节保存在内存的低地址端

大端字节序简写为 BE( Big-Endian ), 表示 高位字节在前,低位字节在后,高位字节保存在内存的低地址端,低位字节保存在在内存的高地址端

 

数据是从高位字节到低位字节显示的,而内存地址是从低地址向高地址增加

则完整的UTF-16编码为:大端:FEEF4E2D;小端:FFEE2D4E。其中的FEEF/FFEE指明了时大端还是小端编码,即为BOM。对于UTF-16和UTF-32,带BOM是必要的,而对于UTF-8,不必须带BOM。微软习惯于在UTF-8文件中放置BOM(从而把UTF-8和ASCII区分开),把带有BOM小端的UTF-16称为“Unicode”。

BOM

BOM 是 byte-order mark 的缩写,用于标记字节序(byte order)在 Unicode 编码中有一个叫做 "零宽度非换行空格" 的字符 ( ZERO WIDTH NO-BREAK SPACE ), 用字符 FEFF 来表示对于 UTF-16 ,如果接收到以 FEFF 开头的字节流, 就表明是大端字节序,如果接收到 FFFE, 就表明字节流是小端字节序UTF-8 没有字节序问题,上述字符只是用来标识它是 UTF-8 文件,而不是用来说明字节顺序的。"零宽度非换行空格" 字符 的 UTF-8 编码是 EF BB BF, 所以如果接收到以 EF BB BF 开头的字节流,就知道这是UTF-8 文件。

参考:UTF-8 UTF-16 UTF-32 比较 - 简书 (jianshu.com)为什么 UTF-8 编码比 UTF-16 编码应用更广泛? - 知乎 (zhihu.com)「带 BOM 的 UTF-8」和「无 BOM 的 UTF-8」有什么区别?网页代码一般使用哪个? - 知乎 (zhihu.com)「带 BOM 的 UTF-8」和「无 BOM 的 UTF-8」有什么区别?网页代码一般使用哪个? - 知乎 (zhihu.com)Unicode、UTF-8、UTF-16 终于懂了 - 知乎 (zhihu.com)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值