關於Java的代碼點(codePoint)、unicode編碼、UTF-8、UTF-16


something before start

看Character和String的時候發現的小知識點,挺好玩的
Java采用的是UTF-16,基本字符(BMP)采用一個bit存儲,增補字符采用倆

unicode

ASCII 碼一共定義了 128 個字符,英語用 128 個字符來編碼完全是足夠的,但是用來表示其他語言,128 個字符是遠遠不夠的。

Unicode是展示世界上所有語言中的所有字符的標准方案,他給所有的字符指定了一個數字用來表示該字符。但是Unicode並沒有規定字符對應的二進制怎么存儲。

Unicode字符平面映射

目前的Unicode字元分為17組編排,每組稱為平面(Plane),而每平面擁有65536(即216)個代碼點。然而目前只用了少數平面。

平面 始末字元值 中文名稱 英文名稱
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

增補字符是代碼點在 U+10000 至 U+10FFFF 范圍之間的字符(上述表格中1號平面~16號平面之間的),也就是那些使用原始的 Unicode 的 16 位設計無法表示的字符。從 U+0000 至 U+FFFF 之間的字符集有時候被稱為基本多語言面 (BMP)。因此,每一個 Unicode 字符要么屬於 BMP,要么屬於增補字符。

因為字符很多嘛,所以就會有的字符需要兩個字節,甚至三四個字節才能表示,如果直接全用四個字節表示那就太浪費空間。於是,為了較好的解決 Unicode 的編碼問題, UTF-8、UTF-16等編碼模式 出現了。

UTF-8

UTF-8 是可變長編碼。它可以使用 1 - 4 個字節表示一個字符,根據字符的不同變換長度。

編碼規則如下:

對於單個字節的字符,第一位設為 0,后面的 7 位對應這個字符的 Unicode 碼點。因此,對於英文中的 0 - 127 號字符,與 ASCII 碼完全相同。

對於需要使用 N 個字節來表示的字符(N > 1),第一個字節的前 N 位都設為 1,第 N + 1 位設為0,剩余的 N - 1 個字節的前兩位都設位 10,剩下的二進制位則使用這個字符的 Unicode 碼點來填充。

Unicode 十六進制碼點范圍 UTF-8 二進制
0000 0000 - 0000 007F 0xxxxxxx
0000 0080 - 0000 07FF 110xxxxx 10xxxxxx
0000 0800 - 0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
0001 0000 - 0010 FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

UCS-2

UCS-2對每一個Unicode碼位使用2bytes字集(16位bit);
UCS-4對每一個Unicode碼位使用4bytes字集(32位bit);
UTF-16可看成是UCS-2的父集。
在沒有輔助平面字符(surrogate code points)前,UTF-16與UCS-2所指的是同一的意思。但當引入輔助平面字符后,就稱為UTF-16了。

UTF-16

代理區

從U+D800到U+DFFF的碼位(代理區)

因為Unicode字符集的編碼值范圍為0-0x10FFFF,而大於等於0x10000的輔助平面區的編碼值無法用2個字節來表示,所以Unicode標准規定:基本多語言平面內,U+D800..U+DFFF的值不對應於任何字符,為代理區。因此,UTF-16利用保留下來的0xD800-0xDFFF區段的碼位來對輔助平面的字符的碼位進行編碼。

但是在使用UCS-2的時代,U+D800..U+DFFF內的值被占用,用於某些字符的映射。但只要不構成代理對,許多UTF-16編碼解碼還是能把這些不符合Unicode標准的字符映射正確的辨識、轉換成合規的碼元. 按照Unicode標准,這種碼元串行本來應算作編碼錯誤.

BMP

從U+0000至U+D7FF以及從U+E000至U+FFFF的碼位

第一個Unicode平面(BMP),碼位從U+0000至U+FFFF(除去代理區),包含了最常用的字符。UTF-16與UCS-2編碼在這個范圍內的碼位為單個16比特長的碼元,數值等價於對應的碼位。BMP中的這些碼位是僅有的碼位可以在UCS-2被表示。

輔助平面

從U+10000到U+10FFFF的碼位,在UTF-16中被編碼為一對16比特長的碼元(即32bit,4Bytes),稱作 code units called a 代理對(surrogate pair),具體方法是:

Ø 碼位減去0x10000, 得到的值的范圍為20比特長的0..0xFFFFF(因為Unicode的最大碼位是0x10ffff,減去0x10000后,得到的最大值是0xfffff,所以肯定可以用20個二進制位表示),寫成二進制形式:yyyy yyyy yyxx xxxx xxxx。

Ø 高位的10比特的值(值的范圍為0..0x3FF)被加上0xD800得到第一個碼元或稱作高位代理(high surrogate), 值的范圍是0xD800..0xDBFF。由於高位代理比低位代理的值要小,所以為了避免混淆使用,Unicode標准現在稱高位代理為前導代理(lead surrogates)。

Ø 低位的10比特的值(值的范圍也是0..0x3FF)被加上0xDC00得到第二個碼元或稱作低位代理(low surrogate), 現在值的范圍是0xDC00..0xDFFF。 由於低位代理比高位代理的值要大,所以為了避免混淆使用,Unicode標准現在稱低位代理為后尾代理(trail surrogates)。

Ø 最終的UTF-16(4字節)的編碼(二進制)就是:110110yyyyyyyyyy 110111xxxxxxxxxx。

codePoint

如上面提到的,在UTF-16中增補字符需要兩個char來表示,比如😃 對應的表示是 \uD83D\uDE03 ,而如果只是截取一半,那這個字符是沒有意義的。

在Java中,一個實際的完整的字符稱作代碼點(codePoint)

下面舉個例子

String testCode = "ab\uD83D\uDE03";
int length = testCode.length();
int count = testCode.codePointCount(0, testCode.length()); //求出0到testCode.length()之間的代碼點數目

System.out.println(testCode);
System.out.println("代碼點數目:"+count);

//輸出
ab😃
代碼點數目:3

所以如果要對字符串進行嚴格的過濾的話,應該按照代碼點來進行訪問,而不是逐位訪問char

舉個栗子:

String testCode = "ab\uD83D\uDE03漢字";
int cpCount = testCode.codePointCount(0, testCode.length());
System.out.println("cpcount:"+cpCount);

for (int index = 0; index < cpCount; ++index) {
  //這里的i是字符的位置
  int i = testCode.offsetByCodePoints(0, index);
  //下個字符位置
  int nexti = testCode.offsetByCodePoints(0,index+1);
  int codepoint = testCode.codePointAt(i);
  String cur = testCode.substring(i,nexti);
                
  System.out.println("i:"+i+" index:"+index+ " cur:" + cur +"\tcodepoint:"+codepoint );
}
//輸出     
cpcount:5
i:0 index:0 cur:a	codepoint:97
i:1 index:1 cur:b	codepoint:98
i:2 index:2 cur:😃	codepoint:128515
i:4 index:3 cur:漢	codepoint:27721
i:5 index:4 cur:字	codepoint:23383


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM