UTF-8,一种对Unicode编码的变长形式的实现,Unicode还包括其他的实现形式比如UTF-16 (BE, LE) ,UTF-32 (BE,LE) 。
提到UTF-8,总能想起来Window里面的从前的记事本,使用UTF-8编码时会向文件开头加一个[BOM]标记,使用十六进制表示就是 EF BB BF 。
UTF-8的实现:
比如大写数字“一”,它的Unicode代码点是 U 4E00 ,它的UTF-8编码的十六进制表示是 E4 B8 80 ,占用了三个字节,阿拉伯数字 1 的编码十六进制形式是 31 ,占用1个字节。
UTF-8编码的单字节编码和多字节编码是有规律可循的。
解释:
单字节:UTF-8编码以0开头,其余7位按顺序全部填入Unicode的代码数字,所以最大只能到 01111111 = 0x7F。
多字节:UTF-8编码均以 1...0 开头,编码的第一个字节起始有几个 1 就表示当前字符占用几个字节,除第一个字节外,其余字节以10开头。所以对于双字节可利用的位数是 5 6 = 11,所以对应Unicode范围 (1000 0000 = 0x80) ~ (0111 1111 1111 = 0x7FF),三、四字节计算同理。(目前Unicode最大范围支持0 ~ 0x10FFFF,1~4个字节)
说到这里回顾上面的“一”的编码, U 4E00 的十六进制 0x4E00,二进制 0100 1110 0000 0000 ,大于0x0800,小于0xFFFF,转换为UTF-8时占用3字节,需使用第三个模板,按顺序填入:
1110 0100 1011 1000 1000 0000
对应转换成十六进制表示 E4 B8 80 。
UTF-16:
存在一个起始字节序标记 FF FE 或 FE FF ,分别代表小端序和大端序,对于“一”的UTF-16(LE)的编码则是004E,数字1的编码为3100,这个内存顺序是小端序,与Unicode顺序是相反的,而UTF-16(BE)则是相反,“一”的编码是4E00,大端序的UTF-16是与Unicode顺序相同的。
图中的文字是“1一1”,编码:UTF-16,默认小端序。
回到开头提到的BOM(Bytes Order Mark) EF BB BF ,转换为Unicode就是 FE FF ,正好与UTF-16的标记相反。
UTF-16实现:
编码:
双字节:Unicode代码数字小于U 10000的,直接转换为UTF-16。
四字节:大于等于U 10000的需要计算,方式如下:
假设字符的Unicode代码为U,最大是0x10FFFF。
令U' = U - 0x10000,那么U'的取值范围就是 0 ~ 0xFFFFF,20bits。
UTF-16四字节分为两部分,高字节位部分以0xD800为模板,低字节位部分以0xDC00为模板,每部分的低字节有10个bit的空余,将U'的20bits平均分成两份,分别填入对应的高字节位和低字节位部分。
U' = yyyyyyyyyyxxxxxxxxxx
W1 = 110110yyyyyyyyyy
W2 = 110111xxxxxxxxxx
最终的UTF-16 编码就是 W1 W2的组合。
例如字符
Unicode代码 U 1D4D1 ,UTF-16(BE) D8 35 DC D1 U' = 0x1D4D1 - 0x10000 = D4D1 = 0000 1101 0100 1101 0001 (需补全20bits),
W1 = 1101 1000 0011 0101 = D8 35
W2 = 1101 1100 1101 0001 = DC D1
则UTF-16(BE)最终结果是 D8 35 DC D1 。UTF-16(LE)的结果就是 35 D8 D1 DC 。
【参考阅读】
https://tools.ietf.org/html/rfc2781
https://tools.ietf.org/html/rfc3629