Unicode
Unicode出现的目的就是为了解决国家地区之间字符编码互相不兼容的情况,所以尽可能多的收集已知的字符。
因为编码的字符很多,所以采用了类似GB2312分区的方式,不过这里的区称为平面,每个平面可以存放216(65536)个符号。
目前暂时有17个平面,所以总共有221个码位,需要3个字节,最常用的字符对应在最前面的65536码位,称为基本平面(BMP),剩下的称为辅助平面(SMP)。
Unicode规定了字符对应的码位,还规定了几种编码方式,就是不同的编码字符集转换字符编码表方式。
UTF-8
最通用的字符编码方式,晚于UTF-16出现。
采用变长编码,可以是1~3个字节。
编码规则如下:
- 0x0~07F码位:编码为1个字节,和ASCII完全一致。
- 0x80~0x7FF码位:编码为2个字节。使用原来码位填充格式:110xxxxx 10xxxxxx
- 0x800~0xFFFF码位:编码为3个字节。使用原来码位填充格式:1110xxxx 10xxxxxx 10xxxxxx
- 0x10000~0x10FFFF码位:编码为4个字节。使用原来码位填充格式:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
特点
- 完全兼容ASCII
- 节省空间
- 第一个字节指明了码单元的所占的字节数
- 后续每个字节都有固定的前缀
- 码单元之间容易区分
UTF-16
基本平面内的码位编码为2个字节,其余使用4个字节。
对于基本平面(恰好是2字节)的码位,不足位在前端补充0即可。
对于其余平面码位,如果直接使用码位顺位下来的数字编码,会造成无法区分当前字节属于哪一种码单元(code unit)。
在基本平面的0xD800~0xDFFF,存在一片没有空余出来的码位,可以利用这个特点区分当前字节是否在是基本平面的编码字节,这段码位如果被占用,则应该是4个字节的其他平面字符编码。
上述空余码位区间被一份为2,形成了两个部分,0xD800~0xDBFF和0xDC00~0xDFFF,二进制形式分别是:0b1101 10HH HHHH HHHH
和0b1101 11L LLLL LLLL
。这就形成了UTF-16中4字节编码的格式:0b1101 10HH HHHH 1101 11L LLLL
,用其他平面的码位经过计算填充格式中的H和L即可。
计算方法
- 码位减去0x10000,得到一个数字
- 转为2进制,填充为20位
- 前10位填充上述H位
- 后20位填充上述L位
计算公式如下
1 | // c为 其他平面的码位 |
辅助平面计算示例
1 | str='A𠮷' |
这个字符串中,A
处在基本平面内,𠮷
处在辅助平面内,所以字符串是6个字节,长度是3。
tempStr2是获取到的𠮷
码点,最后一行的输出可以验证这一点。
那么tempStr3是什么呢?是该字符串最后2个字节的值。可以验证一下。
前面知道了𠮷
码点是134071
,转换成16进制就是0x20bb7
,根据UTF-16编码规则,首先减去0x10000
,得到0x10bb7
,二进制为0b0001 0000 1011 1011 0111
,取后半部分11 1011 0111
填充到低位格式中去,得到0b1101 1111 1011 0111
,就是10进制的57271
。
1 | // 接前一部分代码 |
tempStr5的值为什么是55362
?因为字符串的第3、4字节存储的值就是它。接着上面说的验证步骤,提取0b0001 0000 1011 1011 0111
的前10位填充进高位格式中去,得到0b1101100001000010
,就是10进制的55362
。
UTF-32
最直接的编码方式,用4个字节表示所有码位。浪费存储空间。
UCS-2编码
UCS-2编码早于UTF-16出现,所以被许多编程语言首先支持。
UCS-2只支持基本平面的字符编码,是UTF-16的子集。