JAVA學習腳印10:解惑java 中UTF-16與char
JAVA學習腳印10:解惑java 中UTF-16與char
java中的char、utf-16編碼、程式碼點、程式碼單元等概念,做一個瞭解還是有必要的。
1.基本概念
1) Java的字元型別和字串型別
字元型別採用的是UTF-16編碼方式對Unicode編碼表進行表示。其中一個char型別固定2位元組,為無符號數,表示範圍為'\u0000'(0)~'\uffff'(65,535)。
java中的String定義如下:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
...
}
可見String內部使用char來儲存字元的,且是不可變的,即java字串是由char序列組成。
2) Unicode編碼表的專業術語:
a. 程式碼點 (code point): 指在Unicode編碼表中一個字元所對應的程式碼值。如漢字“一”的程式碼點是U+4E00,英文字母“A”的程式碼點是U+0041。
b. 程式碼單元( code unit): 規定16bits的儲存容量就是一個程式碼單元。java中的一個字元char就對應一個程式碼單元,大多數uncode字元使用一個程式碼單元就夠了,而輔助字元(見下文)需要一對程式碼單元。
可以這樣構造字元資料:
如 char[] chs = {'\u2764','\u2602','\u2600','\u262F','\u262D','\u2622','\u260E'};
上面這幾個字元比較流行,在GUI Jlabel中彩色列印出來如下圖所示:
可以通過網站: http://unicode-table.com/en/#control-character
來獲取你想要的unicode字元。
c. 程式碼級別 (code plane): Unicode編碼表 ,分為17個程式碼級別 (code plane),其中程式碼點U+0000-U+FFFF為第一級別 ——基本多語言級別 (basic multiling l plane),可以用一個程式碼單元儲存一個程式碼點。其餘16個附加級別 從0x10000-0x10FFFF(需要兩個程式碼單元)。
其中需要指出的是在多語言級別中,U+D800-U+DFFF這2048值沒有表示任何字元,被稱為Unicode的替代區域(surrogate area)。UTF-16正是的運用了這一區域,用2個程式碼單元(2*16bits)巧妙的表示出20bits程式碼點的Unicode附加級別。
3)UTF-16編碼演算法
假設U是一個程式碼點,也就是Unicode編碼表中一個字元所對應的Unicode值。
1) 如果U<U+10000,也就是處於Unicode的基本多語言級別中。這樣16bits(一個程式碼單元)就足夠表示出字元的Unicode值。
2) 如果U+10FFFF>U>=U+10000,也就是處於附加級別中。UTF-16用2個16位來表示出了,並且正好將每個16位都控制在替代區域U+D800-U+DFFF 中了。
附加級別中的編碼具體操作如下:
分別初始化2個16位無符號的整數 —— W1和W2。其中W1=110110yyyyyyyyyy(0xD800-0xDBFF),W2 = 110111xxxxxxxxxx(0xDC00-OxDFFF)。
U' = U - 0x10000(注意,網上很多部落格中關於這個演算法的存在錯誤,錯誤之處就是沒有減去0x10000).
然後,將U'的高10位分配給W1的低10位,將U'的低10位分配給W2的低10位。這樣就可以將20bits的程式碼點U拆成兩個16bits的程式碼單元。
而且這兩個程式碼點正好落在替代區域U+D800-U+DFFF中。而且w1和w2具有可區分性,0xD800-0xDBFF屬於高代理項(high-surrogate),0xDC00-OxDFFF屬於低代理項(high-surrogate)。
2.例項說明
這裡舉一個網上很多部落格提到的比較經典的例子,U+1D56B:
該字元可以在 http://www.scarfboy.com/coding/unicode-tool?
網站上輸入程式碼點後檢視,看起來是這樣的,
假設U = U+1D56B ,則U屬於附加級別中的附加字元,那麼可如下計算編碼:
Step1:
U' = U- 0x10000 = 0x0D56B
= 0000 1101 0101 0110 1011
Step2 :
U'高十位: 0000 1101 01
U'低十位: 01 0110 1011
則w1 = 1101 1000 0011 0101 = 0xD835
w2 = 1101 1101 0110 1011 = 0xDD6B
至此將U+1D56B 編碼為兩個程式碼單元,第一個為0xD835,第二個為 0xDD6B 。
下面的程式碼可以加深對上述概念的理解:
package com.learningjava; /** * This programe try to illuminate UTF-16 in java * * for unicode character refer to the following website : * http://www.scarfboy.com/coding/unicode-tool? * * for more details refer to the book 《core java : volume 1》 * * for utf-16 algorithm refer to the following website : * http://en.wikipedia.org/wiki/UTF-16#Example_UTF-16_encoding_procedure * * @author wangdq * 2013-09-26 */ public class UTF16Test { public static void main(String[] args) { String sample = null; if(args.length > 0) { sample = args[0]; } else { // for the sake of not show the character of u+1D56B String special = new String(Character.toChars(0x1D56B)); sample = special+" zZ"; } System.out.println("sample string is: "+sample); // String.length : Returns the length of this string. // The length is equal to the number of Unicode code units in the string. int len = sample.length(); System.out.println("code units count: "+len); // traverse the string by code units System.out.print("code units are: "); UTF16Test.traverseByCodeUnits(sample); System.out.println(); // get the number of Unicode code points int cpCount = sample.codePointCount(0, len); System.out.println("code points count: "+cpCount); // traverse the string by code point s System.out.print("code points are: "); UTF16Test.traverseByCodePoints(sample); System.out.println(); } /** * traverse the string by code points * @param str the specified string to traverse */ public static void traverseByCodePoints(String str) { int cpCount = str.codePointCount(0, str.length()); for(int ix = 0;ix < cpCount;ix++) { printCodePoint(str.codePointAt(ix)); } } /** * traverse the string by code units * @param str the specified string to traverse */ public static void traverseByCodeUnits(String str) { int cuCount = str.length(); for(int ix = 0;ix < cuCount;ix++) { String content = String.format("(%04x) ", (int)str.charAt(ix)).toUpperCase(); System.out.print(content);//get code unit } } /** * print code point in hexadecimal form * @param cp the code point */ private static void printCodePoint(int cp) { //check if is the supplementary code point if(Character.isSupplementaryCodePoint(cp)) { char[] chs = Character.toChars(cp);//stored the code point in UTF-16 representation String content = String.format("[U+%04x,U+%04x] ", (int)chs[0],(int)chs[1]).toUpperCase(); System.out.print(content); } else { String content = String.format("[U+%04x] ",cp).toUpperCase(); System.out.print(content); } } }
不帶引數時執行結果為 :
更多關於字元和字串的操作可參考java官方API.
相關文章
- Java 解惑(Java Puzzler)Java
- java學習:Java中的其它類Java
- java學習中問題與解決方式Java
- java學習之道 --- 如何學習java?Java
- JAVA中“多型”案例學習Java多型
- 如何學習Java? 在學習Java的過程中需要掌握哪些技能?Java
- Java學習筆記7(Java常用類庫與工具 )Java筆記
- Java RMI學習與解讀(一)Java
- Java RMI學習與解讀(二)Java
- Java學習—java-RedisJavaRedis
- Java原始碼閱讀-String中的private final char value[];Java原始碼
- Java學習Java
- Java學習之路 -- Java怎麼學?Java
- java學習中不懂的問題Java
- Java中IO流學習總結Java
- JAVA java學習(24)——————java修飾符Java
- Flutter學習篇(四)—— 尺寸解惑Flutter
- 【java乾貨】10個堪稱神器的 Java 學習網站Java學習網站
- Java註解與反射學習筆記Java反射筆記
- JAVA基礎學習-數字與字串學習總結Java字串
- 【java學習】JDK(Java Development Kit)JavaJDKdev
- JAVA學習--JAVA基礎(一)Java
- Java學習自學Java
- Java學習:反射Java反射
- Java學習方法Java
- 如何學習JavaJava
- java學習之路Java
- java學習11.12Java
- java學習11.15Java
- java學習10.17Java
- java學習10.23Java
- Java學習11.14Java
- java學習11.18Java
- java學習11.19Java
- java學習11.25Java
- java學習11.29Java
- java學習9.21Java
- java學習7Java