Unicode和UTF-8/UTF-16/UTF-32之間就是字元集和編碼的關係。字符集的概念實際上包含兩個方面,一個是字符的集合,一個是編碼方案。字符集定義了它所包含的所有符號,狹義上的字符集並不包含編碼方案,它只是定義了屬於這個字符集的所有符號。但通常來說,一個字元集並不僅僅定義字元集合,它還為每個符號定義一個二進位編碼。當我們提到GB2312或ASCII的時候,它隱式地指明了編碼方案是GB2312或ASCII,在這些情況下可以認為字元集與編碼方案互等。
但是Unicode有多種編碼方案。 Unicode字符集規定的標準編碼方案是UCS-2(UTF-16),以兩個位元組表示一個Unicode字符(UTF-16中兩個位元組的基本多語言平面字符, 4個位元組的為輔助平面字元)。而UCS-4(UTF-32)用4個位元組表示一個Unicode字元。另外一個常用的Unicode編碼方案–UTF-8用1到4個變長位元組來表示一個Unicode字符,並且可以從一個簡單的轉換演算法從UTF-16直接得到。所以使用Unicode字元集時有多種編碼方案,分別用於適當的場景。
再通俗一點地講,Unicode字元集就相當於是一本字典,裡面記載著所有字元(即圖像)以及各自所對應的Unicode碼(與具體編碼方案無關),UTF-8/ UTF-16/UTF-32碼就是Unicode碼經過對應的公式計算得到的並且實際儲存、傳輸的資料。
JVM規格中明確說明了java的char型別所使用的編碼方案是UTF-16,所以先來了解下UTF-16。
Unicode的編碼空間從U+0000到U+10FFFF,共有1112064個碼位(code point)可用來映射字符,,碼位就是字符的數字形式。這部分編碼空間可以分割成17個平面(plane),每個平麵包含2^16(65536)個碼位。第一個平面稱為基本多語言平面(Basic Multilingual Plane, BMP),或稱為第零平面(Plane 0)。其他平面稱為輔助平面(Supplementary Planes)。基本多語言平面內,從U+D800到U+DFFF之間的碼位區塊是永久保留不對應到Unicode字元。 UTF-16就利用保留下來的0xD800-0xDFFF區段的碼位來對輔助平面的字元的碼位進行編碼。
最常用的字元都包含在BMP中,以2個位元組表示。輔助平面中的碼位,在UTF-16中被編碼為一對16位元長的碼元,稱為代理對(surrogate pair),具體方法是:
int character = aString.codePointAt(i); int length = aString.codePointCount(0, aString.length());要注意codePointAt的回傳值,是int而非char,這個值就是Unicode碼。 codePointAt方法呼叫了codePointAtImpl:
static int codePointAtImpl(char[] a, int index, int limit) { char c1 = a[index]; if (isHighSurrogate(c1) && ++index < limit) { char c2 = a[index]; if (isLowSurrogate(c2)) { return toCodePoint(c1, c2); } } return c1; }isHighSurrogate方法判斷下標字元的2個位元組是否為UTF-16中的前導代理(0xD800~0xDBFF):
public static boolean isHighSurrogate(char ch) { // Help VM constant-fold; MAX_HIGH_SURROGATE + 1 == MIN_LOW_SURROGATE return ch >= MIN_HIGH_SURROGATE && ch < (MAX_HIGH_SURROGATE + 1); }
public static final char MIN_HIGH_SURROGATE = '\uD800'; public static final char MAX_HIGH_SURROGATE = '\uDBFF';
然后++index,isLowSurrogate方法判断下一个字符的2个字节是否为后尾代理(0xDC00~0xDFFF):
public static boolean isLowSurrogate(char ch) { return ch >= MIN_LOW_SURROGATE && ch < (MAX_LOW_SURROGATE + 1); }
public static final char MIN_LOW_SURROGATE = '\uDC00'; public static final char MAX_LOW_SURROGATE = '\uDFFF';
toCodePoint方法将这2个码元组装成一个Unicode码:
public static int toCodePoint(char high, char low) { // Optimized form of: // return ((high - MIN_HIGH_SURROGATE) << 10) // + (low - MIN_LOW_SURROGATE) // + MIN_SUPPLEMENTARY_CODE_POINT; return ((high << 10) + low) + (MIN_SUPPLEMENTARY_CODE_POINT - (MIN_HIGH_SURROGATE << 10) - MIN_LOW_SURROGATE); }
这个过程就是以上将一个辅助平面的Unicode码位转换成2个码元的逆过程。
所以,枚举字符串的正确方法:
for (int i = 0; i < aString.length();) { int character = aString.codePointAt(i); //如果是辅助平面字符,则i+2 if (Character.isSupplementaryCodePoint(character)) i += 2; else ++i; }
将codePoint转换为char[]可调用Character.toChars方法,然后可进一步转换为字符串:
new String(Character.toChars(codePoint));
toChars方法所做的就是以上将Unicode码位转换为2个码元的过程。
以上是詳細介紹Java中codepoint和UTF-16相關的一些事的詳細內容。更多資訊請關注PHP中文網其他相關文章!