1. UTF 简介
UTF
(UCS/Unicode/Universal Transformation Format)有多种 transform 方式, 常见的有 UTF-8/UTF-16/UTF-32.
出现原因:
事实证明, 对可以使用
ASCII
表示的字符使用Unicode
并不高效, 因为Unicode
使用 2 个字节. 为了解决这个问题, 出现了一些中间格式字符集, 被称为通用转换格式. 可以这么说Unicode
是编码方式, 它规定了编码(即哪个字符在什么码位),而UTF-8
等是Unicode
的实现方式, 它出于节省空间或其它目的来对Unicode
所占空间进行转换.另外我目前的理解是: Unicode 码原生不支持与任何码表兼容, 包括ASCII.
举例:UCS-2
以 2 字节为单位而ASCII
以 1 个字节为单位, 试想英文a : 0110,0001
和0000,0000 0110,0001
计算机是不会认为他们是一样的. 而如果使用UTF-8那么, 编码就会相同为 1 个字节 0110,0001.
2. UTF-8
UTF-8
(将 8 bit
, 即一个字节看作一个单位): 使用 1~4 个字节来编码. 如, 当用 UTF-8
存储 ASCII
字符时就只用 1 个字节,相似其它字符按一定算法转换为 1~4 个字节. 算法如下
UCS-2编码(16进制) UTF-8 字节流(二进制)
0000 - 007F 0xxxxxxx
0080 - 07FF 110xxxxx 10xxxxxx
0800 - FFFF 1110xxxx 10xxxxxx 10xxxxxx
比如 汉
字的 Unicode
是 6C49
, 那么就需要使用 3 字节的格式,写出来是 1110 0110,1011 0001,1000 1001
, 也即 E6 B1 89
. 4字节算法没写.
3. UTF-16
3.1. 实现方式
UTF-16
将 16 bit
(2 字节) 看作一个单位. 设计之初为固定宽度的 16 bit
(2 byte
) 编码格式(可以表示 plane 0
所有), 随着时间发展为了支持增补字符 (其它plane
) 设置了代理对机制(surrogate pair): 把范围 U+10,0000~U+10,FFFF
内的字符使用一对 (2 个) 16 bit
来表示.
算法如下:
- 对于的UCS码的小于0x10000的部分(plane 0中的),UTF-16编码==UCS-2对应的16位无符号整数.
- 不小于0x10000的部分使用代理对
(具体怎么代理不探究了)。
3.2. big-endian / little-endian
UCS-2
是一个编码方案, 而 UTF-16
是一个实际使用的转换格式. 因为 UTF-16
一个单元是16 bit
, 但计算机只能表示 8 bit
为单位, 所以分解 (解析显示时) 这个单元时这两个 8 bit
谁先谁后就也有说法了 (即一个单位中 2 个字节的字节顺序问题), 高字节到低字节称为大尾 big-endian
, 反之称为小尾 little-endian
. UTF-32
也需要考虑这个问题, 而 UTF-8
以 8 bit
为单位, 故而没有在单位中排字节顺序的需要.
例如: 已知 乙
的 Unicode
编码是 4E 59
, 当我们收到一个 奎
的 Unicode
编码 59 4E
时, 我们是该翻译为奎还是乙呢? 解决方案:
使用 Unicode 的推荐字节顺序标记方法 BOM(Byte Order Mark).
它的方法是: UCS 中有个字符叫 "ZERO WIDTH NO-BREAK SPACE", 它编码为 FE FF, 还有个字符 FF FE 在UCS中不存在。
UCS 规范建议我们在传输字节流最前, 先传输字符 FE FF 表明字节流是 Big-Endian; 传输 FF FE 表明字节流是 Little-Endian.
UTF-8
不需要用 BOM 来表明字节顺序,但是可以用 BOM 来表明编码方式. 字符”ZERO WIDTH NO-BREAK SPACE” 的 UTF-8
编码是 EF BB BF. 如果接受到已此开头的字节流, 那么好了, 你知道它是 UTF-8
编码的.
你好
的 Unicode
编码: 4F 60
, 59 7D
. 下图是用 UTF-8
编码的文本: 你好
两个字的编码:
EF BB BF E4 BD A0 E5 A5 BD
使用 UTF-16 Big-endian
的 你好
编码:
EF FF 4F 60 59 7D
3.3 UTF-8 与 UTF-16 对比
比起UTF-8
, UTF-16
的好处在于大部分字符都是用固定长度 (2 byte
) 存储 (如果长度固定是你的要求的话).