乱码一直是一个很让人头疼的问题,这次往oracle的Blob里面写东西的时候,又出现了乱码。不过这次成功解决了,看了几篇讲java编码格式的文章恍然大悟,原理其实很简单,大家可以到这几个链接看看,他们写的很清楚:
http://lavasoft.blog.51cto.com/62575/273608
http://developer.51cto.com/art/200906/132635.htm
http://topic.csdn.net/u/20080822/11/69048375-6928-4a30-a58a-3ded53dc53f2.html
http://blog.csdn.net/fangao2620/archive/2008/02/25/2120221.aspx
http://jiangzhengjun.iteye.com/blog/512072
http://ayagen.iteye.com/blog/587653
其实,说白了就是,java中的String永远都是unicode编码的,以它作为中间结果转化成各种不同的编码格式,比如:
String str = "中文";
byte[] utf8b = str.getBytes("UTF-8");
byte[] gbkb = str.getBytes("GBK");
// 没有乱码
System.out.println(new String(utf8b, "UTF-8"));
// 没有乱码
System.out.println(new String(gbkb, "GBK"));
// 有乱码
System.out.println(new String(gbkb, "UTF-8"));
// 有乱码
System.out.println(new String(utf8b, "GBK"));
byte数组utf8b和gbkb是不一样的,是转化成各自编码格式后的二进制数组,而new String(utf8b, "UTF-8")与new String(gbkb, "GBK")是一样的都是以Unicode编码保存。
我们可以来分析一下:
1. 当执行以下时:
byte[] utf8b = str.getBytes("UTF-8");
jvm实际上是做了这样的转化 UNICODE => UTF-8,就是将Jvm内存中的unicode编码二进制码转化成UTF-8格式的二进制码然后赋值给byte[] utf8b 。这个转化的过程我们不用管,jvm会根据一个编码格式对照表来转化。
2. 当执行后面代码:
new String(utf8b, "UTF-8")
实际上第二个参数"UTF-8"告诉jvm:“当前utf8b的编码格式是"UTF-8",你就以这个格式转化成unicode吧!”。也就是将utf8b转化成unicode再存入Jvm的内存,utf8=>unicode。(这个参数应该是为了告诉jvm使用“UTF-8”的编码格式对照表来转化)
很多时候出现乱码实际上就是第二个过程出现了问题,比如有个页面是以“UTF-8”方式编码的,当你提交了一个表单后,浏览器将表单的内容以“UTF-8”格式编码后以HTTP POST请求的方式发送到后台。当HTTP Post请求到达servlet后,提交的表单数据都是“UTF-8”编码的二进制数据。当你调用request.getParameter("someParam")的时候,servlet实际上将inputstream中“someParam”的数据取出,然后将其转化成字符转。为了便于理解,我们假设这段数据是一个byte[] pb,当你调用了request.getParameter("someParam")时相当于调用了new String(pb),由于你没有指定使用何种方式编码,因此,jvm使用系统默认的编码格式编码,这时如果你的系统默认编码格式是“UTF-8”那就相当与调用了
new String(utf8b, "UTF-8")
不会出现乱码。但是如果你很倒霉,你的默认编码格式是“GBK”那么就好比调用了
new String(utf8b, "GBK")
乱码就不可避免了。因为,jvm会试图将原来是"UTF-8"格式的数据使用"GBK"的对照表来转化成unicode,结果当然是牛头不对马嘴了。