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 0xxxxxxx0080 - 07FF 110xxxxx 10xxxxxx0800 - 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) 存储 (如果长度固定是你的要求的话).
