java中一个字符到底多少个字节

10月 31, 2014 |

前言:
如果你一看这个标题的第一反应是“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 对应一个字符,对于扩展集中的字符,而是一个对应两个字符。不信?请看下面的示例:

输出为:
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才含义该字符。)。
修改我们的示例代码为:

由于“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.