猜一下这段java 代码结果输出什么?
System.out.println("𐐀".length());
Unicode 字符定义
Unicode 定义了全球所有文字(字符)的编码标准,一个字符对应一个编码(数字)。最开始使用2个字节来表示所有字符,这也是为什么java char 使用两个字节来表示字符的原因,但是2个字节不能完全表示所有的字符,所以编码后面扩展到了3个字节。
1、 基本字符表示从 U+0000 到 U+FFFF 之间的字符集,也被成为基本多语言面(BMP)
2、增补字符表示从 U+010000 到 U+10FFFF 范围之间的字符集。
March 2020, there is a repertoire of 143,859 characters.
Private Use Area: U+E000–U+F8FF (6,400 characters).
Supplementary Private Use Area-A: U+F0000–U+FFFFD (65,534 characters).
Supplementary Private Use Area-B: U+100000–U+10FFFD (65,534 characters).
unicode 编码扩展了,java 的char 怎么办,表示不了了?
The Java platform uses the UTF-16 representation in char arrays and in the String and StringBuffer classes. In this representation, supplementary characters are represented as a pair of char values, the first from the high-surrogates range, (\uD800-\uDBFF), the second from the low-surrogates range (\uDC00-\uDFFF).
java 默认使用UTF-16 来进行unicode 编码,如果unicode 大于了2个字节,java 使用两个char 来表示。上面示例中 “𐐀” unicode == U+10400,所以会被解析成两个char。
Unicode 相关术语
character :
书写系统中文本的最小表示单元
character set :
character 集合
code point:
代码点,unicode 字符对应的编码的数字,比如U+10400 就是一个代码点
code space :
unicode 代码点的范围,U+0000 -- U+10FFFF
code unit :
对代码点进行二次编码的最小单位(1 byte,2 byte ,4 byte),一个字符可能由多个code unit 编码组成,见后续unicode 实现。
codepage、codemap:
表示码表,编码与字符的映射关系
Unicode 实现
Unicode 标准定义了一个字符对应一个数字,这个数字可以是2个字节,也可以是3个字节。我们程序要对这个数字进行解析,就需要判断是2个字节组成一个字符,还是三个字节组成一个字符。常用的编码方式有 UTF-8、 UTF-16、 UTF-32,如果是需要在网络中传输,还需求定义大端小端。
UTF-8
code unit 为1个字节,UTF-8 编码unicode 后结果为1-4 个字节,这样我们就可以从第一个字节来判断后续还有多少个字节。
UTF-16
code unit 为2个字节,UTF-16编码unicode 后结果为 2、4 个字节
UTF-32
code unit 为4个字节,UTF-32编码unicode 后结果为 4 个字节
例如:
"abc" U+10400
UTF-8编码:61, 62, 63, f0, 90, 90, 80
UTF-16编码:0061, 0062, 0063, d801, dc00
UTF-32编码:00000061, 00000062, 00000063, 00010400
Java 字符集实现
java 源文件编码可以定义文件编码方式 ,比如gbk,utf-8,utf-16。java编译器把java 源文件统一编译成unicode(UTF-16)格式存储。可以使用 javac -encoding encoding 自定源文件的编码。
java 是怎么实现编码格式的转换的呢?
java 会对每一种字符集编码与unicode 编码做一个对应关系,这样只要我们知道任何一种编码,都能够转换为unicode 编码。
例如 GBK
b2cStr :就是一个codepage (码表),里面定义所有中文字符与unicode的对应关系。
b2cStr 为什么定义一个字符数组,因为gbk 编码 为一个数组 例如娩 编码为C3E4,用数组可以方便映射。
代码测试
Charset charset =Charset.forName("gbk");
ByteBuffer buffer = ByteBuffer.wrap(new byte[]{(byte)0xc3,(byte)0xe4});
CharBuffer decode = charset.decode(buffer);
IntStream chars = decode.chars();
chars.forEach(i->{
//ux5a29
System.out.println(i);
});
System.out.println(charset.decode(buffer));
输出
娩
char 的值0x5a29 对应的unicode 编码也为娩
输入法与字符集
1、输入法 依据键盘点位查找操作系统对应的字符集,并且找到字符集对应的编码
2、输入法把codepoint 传递给应用程序,应用程序按照指定编码方式对codepoint 进行编码
3、应用程序编码后,存储到磁盘。
比如在windows 系统中,我们可以自定义字符,然后关联一个codepoint ,输入法选择使用codepoint 方式输入。输入对应的codepoint 后,就可以显示为自己构造的字符。应用程序接受到codepoint 后,按照指定的编码方式进行编码。
参考资料:
https://en.wikipedia.org/wiki/Unicode
http://unicode.org/glossary/#coded_character
https://en.wikipedia.org/wiki/Glyph
https://en.wikipedia.org/wiki/Character_encoding
https://en.wikipedia.org/wiki/GBK_(character_encoding)
http://www.khngai.com/chinese/charmap/tblgbk.php?page=0
http://tools.jb51.net/table/gbk_table
https://docs.oracle.com/javase/7/docs/api/java/lang/Character.html