前言:
如果你一看这个标题的第一反应是“java使用unicode编码,地球人都知道是2个字节呀”,如果你的认识也是这样的,看完本文一定会有所收获,如果你的回答是在内存中有些字符使用2个字节表示,有些使用4个字节表示,那么你的认识完全碾压本博文的内容,可以笑笑的离去。
注:由于我当前的wordpress使用的mysql 不支持unicode扩展字符集,所以只能用0x20001 来代替具体的字符。大家在运行代码的时候content:|0x20001| 输出的是具体的字符。
在java.lang.Character类的javadoc中,对这个问题有完美答案,大致为:
一个字符的Unicode编码的专业术语叫code point, 如果一个字符的编码属于U+0000 到 U+FFFF范围,那么可以使用2个字节表示,这个范围不能表示全天下所有的字符,比如“0x20001”。unicode在这个的基础上制定了一个扩展集,对于的范围为U+10000到U+10FFFF,这个范围的字符有个名字叫做“supplementary characters”,它们将占用四个字节。所以在java中并不是一个char 对应一个字符,对于扩展集中的字符,而是一个对应两个字符。不信?请看下面的示例:
1 2 3 4 5 6 |
public static void main(String[] args) throws Exception { char[] arr = Character.toChars(0x20001); System.out.println("char array length:" + arr.length); System.out.println("content:|" + new String(arr) + "|"); System.out.println("String length:" + new String(arr).length()); } |
输出为:
char array length:2
content:|0x20001|
String length:2
看到了吧,确实如此,这也解释了为什么Character类的很多函数都有参数为int的版本,因为有些字符是不能用一个char表示的。
下面我们再看看String.getBytes()方法
因为很多童鞋都认为String.getBytes().length返回的值是该字符在内存中对应的字节数的证据,其实不然。String.getBytes()是使用系统默认字符集处理后的结果,这个可以查看相应源码确认。在启动程序时我们可以通过更改file.encoding属性(“java -Dfile.encoding=utf-8 待执行的类”), 在Eclipse中可以通过Run configurations->Common->Encoding来更改
,如图:
注(encoding的Other 选项是支持输入的,当没有“GBK”选项时可以手动输入这个值然后单击确定。还有“0x20001”没有收录在GBK当中,范围比它还大的GB18030才含义该字符。)。
修改我们的示例代码为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public static void main(String[] args) throws Exception { //char[] arr = Character.toChars(0x20001); String s = new String("知"); //System.out.println("char array length:" + arr.length); System.out.println(s); System.out.println("String length:" + s.length()); System.out.println("default charset:" + Charset.defaultCharset()); System.out.println(s.getBytes().length); for(byte b : s.getBytes()) { System.out.print(toHex(b)); } } public static String toHex(byte b) { char[] hexs = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; String s = ""; s += hexs[(b >> 4) & 0xf]; s += hexs[b & 0xf]; return s; } |
由于“0x20001”太特别了,基本每种编码都对其进行了特殊的处理,GB18030,utf-8, utf-16BE对对其使用了四个自己存放,在上面的例子中我们使用了一个普通的“知”作为示例,我们发现当编码为gbk18030时输出的长度为2,当编码为utf-8时长度为3。
结束语:
语言编码从ANSI的各自为营到unicode一统江湖,这是一个历史的演变。有时它给我们的工作带来了小小的麻烦,但是当我们熟悉它,就可以从容面对。
参考文档:
1.java.lang.Character javadoc
2.rfc2781.txt (utf-16的规范,百度一下就能找到)
3.http://blog.charlee.li/unicode-intro/#content_2_6 ( 一个高手在n年前对各自编码的总结)
Posted in: java基础
Comments are closed.