先来介绍几个基本概念:
码点 : 一个编码表中的某个字符对应的代码值。比如在Unicode字符集里'中'对应是20013,'a'对应97。
Unicode编码:用16进制来书写码点,前面加上U+。码点可以分为17个代码级别。第一个级别是U+0000 ~ U+FFFF。称为基本的多语言级别。剩余的16个代码级别从U+10000 ~ U+10FFFF。包涵辅助字符。
我们可以看到,对于U+0000 ~ U+FFFF范围内的字符,用2个字节就可以放下,但是对于U+10FFFF的字符,最少也需要3个字节。如果都用3个字节,会浪费空间,如果都用2个字节,又不满足需求。Java采用了UTF-16变长编码方式。
UTF-16编码算法:
UTF-16编码方法是这样的,它会将U+10000 ~ U+10FFFF范围的字符,高位映射到U+D800 ~ U+DBFF,低位映射到U+DC00 ~ U+DFFF。而U+0000 ~ U+FFFF内U+D800~ U+DFFF是空着的,不表示任何字符。这样就可以判断U+D800~ U+DFFF内是需要4个字节,也就是两个char,其他的都是一个char。
转换演示:
U+1D546 --> U+D835 + U+DD46
1D546高于FFFF,首先减去10000
用2进制表示:
1D546 - 10000 = 0D546 = 0000 1101 0101 0100 0110取出高10位,补0后与D800进行或运算
高10位:0000 1101 01
补0后: 0000 0000 0011 0101
D800: 1101 1000 0000 0000
或运算后:1101 1000 0011 0101 = D835取出低10位与DC00进行或运算
低10位:01 0100 0110
补0后: 0000 0001 0100 0110
DC00: 1101 1100 0000 0000
或运算后:1101 1101 0100 0110 = DD46
拓展思考:
- 映射算法巧妙之处在那里?
U+10000 ~ U+10FFFF一共有2^16 * 2*4种可能性。一对一映射肯定放不到U+FFFF里面。先减去10000好处在于,将数字范围缩减到U+00000 ~ U+0FFFF之间。然后20位直接对半分,将2^20种可能,变成2^10 * 2 ^10,单个映射空间缩减到2^10大小。 - 高低位为什么不映射到一个空间?
我的理解: 解读时要对高低位有个区分,因为不同机器,高低位顺序可能不一致,有的高位在前,有的高位在后。