猜一下這段java 代碼結(jié)果輸出什么?
System.out.println("??".length());
Unicode 字符定義
Unicode 定義了全球所有文字(字符)的編碼標準,一個字符對應(yīng)一個編碼(數(shù)字)。最開始使用2個字節(jié)來表示所有字符,這也是為什么java char 使用兩個字節(jié)來表示字符的原因,但是2個字節(jié)不能完全表示所有的字符,所以編碼后面擴展到了3個字節(jié)。
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個字節(jié),java 使用兩個char 來表示。上面示例中 “??” unicode == U+10400,所以會被解析成兩個char。
Unicode 相關(guān)術(shù)語
character :
書寫系統(tǒng)中文本的最小表示單元
character set :
character 集合
code point:
代碼點,unicode 字符對應(yīng)的編碼的數(shù)字,比如U+10400 就是一個代碼點
code space :
unicode 代碼點的范圍,U+0000 -- U+10FFFF
code unit :
對代碼點進行二次編碼的最小單位(1 byte,2 byte ,4 byte),一個字符可能由多個code unit 編碼組成,見后續(xù)unicode 實現(xiàn)。
codepage、codemap:
表示碼表,編碼與字符的映射關(guān)系
Unicode 實現(xiàn)
Unicode 標準定義了一個字符對應(yīng)一個數(shù)字,這個數(shù)字可以是2個字節(jié),也可以是3個字節(jié)。我們程序要對這個數(shù)字進行解析,就需要判斷是2個字節(jié)組成一個字符,還是三個字節(jié)組成一個字符。常用的編碼方式有 UTF-8、 UTF-16、 UTF-32,如果是需要在網(wǎng)絡(luò)中傳輸,還需求定義大端小端。
UTF-8
code unit 為1個字節(jié),UTF-8 編碼unicode 后結(jié)果為1-4 個字節(jié),這樣我們就可以從第一個字節(jié)來判斷后續(xù)還有多少個字節(jié)。

UTF-16
code unit 為2個字節(jié),UTF-16編碼unicode 后結(jié)果為 2、4 個字節(jié)

UTF-32
code unit 為4個字節(jié),UTF-32編碼unicode 后結(jié)果為 4 個字節(jié)
例如:
"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 字符集實現(xiàn)
java 源文件編碼可以定義文件編碼方式 ,比如gbk,utf-8,utf-16。java編譯器把java 源文件統(tǒng)一編譯成unicode(UTF-16)格式存儲??梢允褂?javac -encoding encoding 自定源文件的編碼。
java 是怎么實現(xiàn)編碼格式的轉(zhuǎn)換的呢?
java 會對每一種字符集編碼與unicode 編碼做一個對應(yīng)關(guān)系,這樣只要我們知道任何一種編碼,都能夠轉(zhuǎn)換為unicode 編碼。
例如 GBK
b2cStr :就是一個codepage (碼表),里面定義所有中文字符與unicode的對應(yīng)關(guān)系。


b2cStr 為什么定義一個字符數(shù)組,因為gbk 編碼 為一個數(shù)組 例如娩 編碼為C3E4,用數(shù)組可以方便映射。

代碼測試
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 對應(yīng)的unicode 編碼也為娩

輸入法與字符集
1、輸入法 依據(jù)鍵盤點位查找操作系統(tǒng)對應(yīng)的字符集,并且找到字符集對應(yīng)的編碼
2、輸入法把codepoint 傳遞給應(yīng)用程序,應(yīng)用程序按照指定編碼方式對codepoint 進行編碼
3、應(yīng)用程序編碼后,存儲到磁盤。
比如在windows 系統(tǒng)中,我們可以自定義字符,然后關(guān)聯(lián)一個codepoint ,輸入法選擇使用codepoint 方式輸入。輸入對應(yīng)的codepoint 后,就可以顯示為自己構(gòu)造的字符。應(yīng)用程序接受到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