ChartSet Encoding in Java
概述
初学Java的人,往往会被Java中的字符编码方式(Charset Encoding)搞的晕头转向。经常就有中文字符显示不正常的情况发生。有的时候,通过调整程序或者系统的某些参数,能够使得程序正常工作,但是一但更换了运行环境,程序往往还存在问题。在这篇短文中,作者试图对Java中的字符编码机制作一个简单的剖析,试图给读者一个清晰的概念。
Byte数组和Char数组
早期在JavaSoft从事JDK开发工作的工程师,大多师原来Sun的Unix开发人员。大家知道,在Unix上,每一个输出设备都对应一个设备文件,如果要向输出设备中输出信息,只要向这个设备文件中写入相应的字节流(Byte Array)即可。早期的Java开发明显是受了这种编程方式的影响,所以在JDK中,输入 /输出都是针对字节流的(在java.io package中的*InputStream和*OuputStream类)。但是,我们知道为了更好的解决I18N问题,Java在内部是使用Unicode进行字符编码的,Unicode(Unicode16)是使用16bit的Char来代表一个字符,用一个Char数组来代表一个字符串。这就要求在输入/输出设备所要求或提供的字节数组和Java内存所要求的字符数组间进行转换。在Java中,java.io Package下的*Reader和*Writer类就是负责此项工作的。在初始化一个Reader或Writer类的时候,我们可以指定它所使用的Encoding方法。比较常用的Encoding类型有:ISO-8859-1,GBK,UTF8等。Reader和Writer类使用相应的Encoding方法完成字节数组和字符数组之间的转换。使用不同的Encoding方式,所得的结果是不同的;如果不使用对应的Decoding方式,就会得到乱字符。比如:如果用GBK Encode,而用UTF8 Decode,肯定会得到错误字符。
下面举一个常见的Web开发中的例子:一般在Web开发中,会使用一些模版(Template),常用的Template Engine有:Velocity,FreeMarker。在模版文件中,会包含有中文字符,为了简化页面模版的编辑过程,这个文件通常使用GBK编码方式,而不是用Unicode编码方式的(支持GBK的编辑器毕竟是比较常见的)。这样,在Java读取文件的时候,就要使用GBK Encoding的Reader。在经过Java的服务器端程序处理之后,会把页面内容输出到浏览器。如果在HTML的Header中指定了content-type=text/html; UTF8的话,就要把HttpServletResponse的Encoding方式设为UTF8方式。这样才能在页面上显示正确的结果。其过程如下所示:
GBK UTF8
硬盘文件 ---------Java VM ----------页面内容
(GBK编码) (Unicode)
所以,我们平时经常说的ISO-8859,GBK,UTF8等,实际上都是指的是Java中Byte数组和Char数组之间的某种转换方式。掌握好了这个原则,Encoding问题即可迎刃而解。
值得一提的是,缺省情况下,运行在不同操作系统上的Java VM所使用的Encoding方式,还是有一些区别的。例如在Windows XP上,Java缺省的Encoding方式使用的是系统的Locale设置(可以在Windows的控制面板中设置);在Solaris,Aix上,缺省是受 -Dfile.encoding=***控制的,如果不指定该VM参数,一般使用的是iso-8859。在编程的时候,做好显示指定缺省的Encoding方式(或者可以通过参数进行调整),不要依赖于Java VM的缺省行为;否则,在不同类型的操作系统之间移植应用程序,很有可能出现编码错误的问题。