Java中的String
Java是完全支持utf-8的。但是,新手在使用Java的时候却总是遇到各种各样的乱码问题。其实根源只有一个,byte[]到char[]的转换和char[]到byte[]的转换。
下文中所有的“字符串”均代表逻辑意义上的字符串,而非String类或String类的实例。
char类型占用两个字节,永远用utf-8编码来表示一个字符,而byte类型则对应代表一个字节。因此,仅仅一个byte[]是无法代表一个字符串的,因为我们还不知道它的编码方式。有可能是utf-8,也有可能是gb2312、iso-8859-1。
在任何数据传输中,都是对应于byte进行的,包括网络IO、文件IO、控制台IO等等。此时,所有的字符串都得用某种编码方式用byte[]表示出来。对于String类来说,就是它的方法getBytes(charset)。调用这个方法,就完成了从char[]到byte[]的转换。
如何完成byte[]到char[]的转换呢?根据上文我们可以知道,这需要明确的知道byte[]是用什么编码来表示这个字符串。然后,使用String(bytes, charset)的构造器,它会根据你所指明的编码方式,把byte[]转换成char[]。
看起来很简单,但实际情况比这个复杂一些。
第一,IO的时候通常只有一端在你的程序中,另外一端,完全可能是C代码,例如MySQL。这时的问题就集中在弄清对方使用的编码方式,并在自己的程序中使用正确的构造器和getBytes(charset)方法。
第二,有时候我们借用一些库来进行IO,或者IO是某个框架的一部分。由于老外的失误,没有考虑编码的问题。因此,它给你的字符串是用操作系统默认的字符集来解释的。此时,我们可以按这个错误的字符集重新调用一次getBytes(charset),把它还原为byte[],再调用正确的String(bytes, charset)构造器。
第三,其实最麻烦的是控制台IO。如果控制台的编码方式不对的话,很难在屏幕上显示出中文来,因为System.out是用native方法构建的。给Java虚拟机设置一些启动参数可以解决这个问题。
最后说一下OutputStream和Writer的关系。OutputStream比Writer底层一点,因为前面也说了,所有的IO最终都要转换到byte[]。Writer的操作对象是char、char[]、String等,他们都会完成一个类似于String到byte[]的转换,并把转换结果发送给OutputStream1。使用中,为了避免编码出现问题,不应当使用FileWriter类。而是FileOutputStream + OutputStreamWriter + PrintWriter。这是因为OutputStreamWriter类的构造器是可以带charset参数的。所有可以直接用OutputStream直接构造的其它Writer也应该在中间塞一层OutputStreamWriter并指明编码集。这样,utf-8的String可以转换到任何编码集输出。同理,InputStream和Reader也是如此。
注1:这并非必然的,毕竟OutputStream和Writer只是两个接口,具体怎么实现还是要看自己。要是哪天ISO规范了字符串的传输标准,或者有个诡异的硬件可以帮助Java直接传输字符串,到时候Writer和OutputStream也脱钩了。不过,在这之前,它们在逻辑上是紧紧关联的,如果你负责这些库的编码的话,体现这种现实关系自然是最好的。
暂时就这么多。thanks for reading.
Filed under: 编程题 on August 25th, 2008 | No Comments »
