Java的char类型和Unicode
最近注意到 Java 中char
类型占用2个字节(16位长),而 Unicode 的编码范围是U+000000
-U+10FFFF
(21位长),两者之间有怎样的关系呢?
Unicode简介
Unicode是一个字符集,它对世界上大部分的文字系统进行了整理、编码,为每一个字符而非字形定义唯一的整数,这个整数称为码点(Code Point)。
20世纪80年代,为了给全世界所有的文字字符统一编码,出现了两个标准化组织:
-
国际标准化组织(ISO),是于1984年创建ISO/IEC JTC1/SC2/WG2工作组。试图制定一份“通用字符集”(Universal Character Set,简称UCS),并最终制定了ISO 10646标准。
-
统一码联盟(Unicode Consortium),由Xerox、Apple等软件制造商于1988年组成,并且制定了Unicode 标准。
后来他们发现对方的存在,大家为着相同的目的工作。于是在 1991 年,他们开始合并双方的工作成果,并为创立一个单一编码表而协同工作。虽然实际上两者的字集编码相同,但实质上两者确实为两个不同的标准。Unicode 1.1 对应于 ISO 10646-1:1993,Unicode 3.0 对应于 ISO 10646-1:2000,Unicode 3.2 对应于 ISO 10646-2:2001,Unicode 4.0 对应于 ISO 10646:2003,Unicode 5.0 对应于 ISO 10646:2003 及附录 1–3。
Unicode 平面
当前的 Unicode 字符分为17组编排,每组称为一个平面(Plane),而每一个平面拥有65536(即$2^{16}$)个码点。然而当前只用了少数平面。在表示一个 Unicode 的字符时,通常会用“U+”然后紧接着一组十六进制的数字来表示这一个字符。
平面 | 始末字符值 | 中文名称 | 英文名称 |
---|---|---|---|
0号平面 | U+0000 - U+FFFF |
基本多文种平面 | Basic Multilingual Plane,简称BMP |
1号平面 | U+10000 - U+1FFFF |
多文种补充平面 | Supplementary Multilingual Plane,简称SMP |
2号平面 | U+20000 - U+2FFFF |
表意文字补充平面 | Supplementary Ideographic Plane,简称SIP |
3号平面 | U+30000 - U+3FFFF |
表意文字第三平面 | Tertiary Ideographic Plane,简称TIP |
4号平面 至 13号平面 | U+40000 - U+DFFFF |
(尚未使用) | |
14号平面 | U+E0000 - U+EFFFF |
特别用途补充平面 | Supplementary Special-purpose Plane,简称SSP |
15号平面 | U+F0000 - U+FFFFF |
保留作为私人使用区(A区) | Private Use Area-A,简称PUA-A |
16号平面 | U+100000 - U+10FFFF |
保留作为私人使用区(B区) | Private Use Area-B,简称PUA-B |
UTF
UTF是 Unicode/UCS Transformation Format 的缩写,是将 Unicode 码点映射到唯一字节序列的算法,根据映射方法的的不同,有 UTF-8、UTF-16 和 UTF-32 等具体的编码格式。
UTF-16
UTF-16把 Unicode 码点映射为16位长的整数(即 Code Unit,码元)的序列。UTF-16使用单个16位码元对最常见的63K字符进行编码,并使用一对16位码元(称为代理)对 Unicode 中不常用的1M字符进行编码。
Unicode 的第一个平面称为基本多语言平面(Basic Multilingual Plane, BMP),或称第零平面(Plane 0),其他平面称为辅助平面(Supplementary Planes)。基本平面内,从U+D800
到U+DFFF
之间的码点区段是永久保留不映射到Unicode字符。UTF-16就利用保留下来的0xD800
-0xDFFF区
块的码点来对辅助平面的字符的码点进行编码。
Unicode 编码范围 | UTF-16 编码形式(二进制) |
---|---|
U+000000 - U+00FFFF |
xxxx xxxx xxxx xxxx |
U+010000 - U+10FFFF |
1101 10yy yyyy yyyy 1101 11xx xxxx xxxx |
可以看到,UTF-16 用二个字节来表示基本平面,用四个字节来表示辅助平面。
基本平面中的码点,UTF-16编码为16位的单个码元,数值等价于对应的码点。
辅助平面中的码点,在UTF-16中被编码为一对16位长的码元(即32位,4字节),称作代理对(Surrogate Pair),具体方法是:
- Unicode 码点减去
0x10000
,得到的值的范围为20位长的0...0xFFFFF
- 将高位的10位的值(值的范围为
0...0x3FF
)加上0xD800
(11011000 00000000
) 得到第一个码元或称作高位代理(high surrogate),值的范围是0xD800...0xDBFF
。由于高位代理比低位代理的值要小,所以为了避免混淆使用,Unicode标准现在称高位代理为前导代理(lead surrogates)。 - 将低位的10位的值(值的范围也是
0...0x3FF
)加上0xDC00
(11011100 00000000
) 得到第二个码元或称作低位代理(low surrogate),现在值的范围是0xDC00...0xDFFF
。由于低位代理比高位代理的值要大,所以为了避免混淆使用,Unicode标准现在称低位代理为后尾代理(trail surrogates)。
由于前导代理、后尾代理、BMP中的有效字符的码点,三者互不重叠,搜索是简单的:一个字符编码的一部分不可能与另一个字符编码的不同部分相重叠。这意味着UTF-16是自同步(self-synchronizing)的:可以通过仅检查一个码元来判定给定字符的下一个字符的起始码元。
UTF-8
UTF-8是一种可变长度字符编码格式,它的码元是8位的,即 UTF-8 把 Unicode 码点映射为字节序列。其编码形式如下表
码点的位数 | 码点范围 | 字节序列 | Byte 1 | Byte 2 | Byte 3 | Byte 4 |
---|---|---|---|---|---|---|
7 | U+0000 - U+007F |
1 | 0xxxxxxx |
|||
11 | U+0080 - U+07FF |
2 | 110xxxxx |
10xxxxxx |
||
16 | U+0800 - U+FFFF |
3 | 1110xxxx |
10xxxxxx |
10xxxxxx |
|
21 | U+10000 - U+10FFFF |
4 | 11110xxx |
10xxxxxx |
10xxxxxx |
10xxxxxx |
每个使用 UTF-8 存储的字符,除了第一个字节外,其余字节的最高两位都是以"10"开始。
在ASCII码的范围,用一个字节表示,超出 ASCII 码的范围就用多个字节表示,这就形成了我们上面看到的UTF-8的表示方法。这样的好处是当文本文件中只有 ASCII 字符(ASCII 编码与其在 Unicode 中的编码相同)时,存储的文件每个字符都为一个字节,与旧的普通 ASCII 文件无异,读取的时候也是如此,所以能与以前的 ASCII 文件兼容。
大于 ASCII 码的,就会由第一字节的前几位表示该字符编码字节序列的长度,比如110xxxxx前三位的二进制表示告诉我们这是个2字节长的序列;1110xxxx是个3字节长的的序列,依此类推;xxx的位置由字符编码的二进制表示的位填入。越靠右的x具有越少的特殊意义。只用最短的那个足够表达一个字符编码数的多字节串。在多字节串中,第一个字节的开头"1"的数目就是该字符编码字节的数目。
Java中的char类型
Java 中的 char
类型的长度为2字节,其存放的是 Unicode 码点,范围为U+0000
- U+FFFF
,即 Java 中的 char
类型只能表示 Unicode 标准中的基本平面(BMP)的字符。由于早期的 Unicode 标准(1991年到1995年,Unicode 2.0以前)使用了固定16位长的编码形式,char
类型长度设计为16位也就不难理解了。
另外 Java 中的字符串使用了UTF-16 的编码(The Java programming language represents text in sequences of 16-bit code units, using the UTF-16 encoding.),此处指的是 Java 语言的内码是UTF-16 编码。所谓内码,指的是程序内部使用的字符编码,特别是某种语言的字符和字符串在内存里用的编码;相对地,外码是程序与外部交互时外部使用的字符编码。
参考资料
https://zh.wikipedia.org/wiki/Unicode
https://zh.wikipedia.org/wiki/UTF-16
https://zh.wikipedia.org/wiki/UTF-8
https://docs.oracle.com/javase/specs/jls/se13/html/jls-3.html
- 原文作者:Kaay
- 原文链接:https://kkua.github.io/post/java-char-type-and-unicode/
- 版权声明:本作品采用知识共享 署名-非商业性使用-禁止演绎(CC BY-NC-ND) 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。