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序列组成。
可以这样构造字符数据:
如 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.
本文参考了博客:blog.csdn.net/fantasy0126/article/details/6050087