最开始接触web的时候,因为Tomcat服务器默认的URIEncoding是ISO8859-1。应该产生过中文乱码问题,某些前辈就会告诉你,可以手动转码。new String(str.getBytes("IOS8859-1"),"gbk");
如果页面编码方式是gbk,Tomcat URIEncoding是ISO8859-1。这样编码再解码,看着确实合理,而且能解决问题。但是其实手动转码局限性是比较大的。
基本概念介绍编码是干什么
编码就是把字符按照规定的字符集转换成 0 1 的字节流形式.解码是什么Java中的字符都是按照Unicode字符集进行编码的。
是把相应的0 1字节流按照规定字符集转换成相应字符的形式。
也就是说,JVM管理的内存中的,字符都是以Unicode编码形式存在的。而非JVM的内存中,字符是以各种各样的字符集进行编码的,当Java程序与外界交互的时候(如I/O),就会涉及到编码解码问题。在Java中,不同的字符集都是以Unicode作为桥梁相互转换的。
- String两个常用API原理介绍。
结果:-52 -20public static void main(String[] args) throws Exception { String str = "天"; byte[] b = str.getBytes("gbk"); show(b); } public static void show(byte[] b){ for (int i = 0; i < b.length; i++){ System.out.print( b[i] + " "); } System.out.println(); }
byte[] b = str.getBytes("gbk");
这句话的意思是把str中的字符 天 用gbk字符集进行编码,返回编码后的字节数组。把-51 -20的2进制的表示形式就是 天 在gbk字符集中的编码。
内存中的操作方式如下相当于从内存中取出天字的Unicode编码,然后利用Unicode到gbk的转换算法,得到天字的gbk编码
如图String str2 = new String(b,"gbk");
这句话的意思是把字节数组b,用gbk字符集解码生成新的字符串。实际上是把字节数组b通过gbk到unicode的转换算法转换成unicode码如图2
手动转码public class Test4 { public static void main(String[] args) throws Exception { String str = "天"; byte[] b = str.getBytes("gbk"); show(b); String str2 = new String(b,"utf-8");//类似I/O时,用了与发送方不匹配字符集。导致下面需要手动转码 byte[] b2 = str2.getBytes("utf-8");//企图转回来 show(b2); System.out.println(new String(str2.getBytes("utf-8"),"gbk")); } public static void show(byte[] b){ for (int i = 0; i < b.length; i++){ System.out.print(b[i]+ " "); } System.out.println(); } }
结果如下:-52 -20
-17 -65 -67 -17 -65 -67
锟斤拷上面的过程与我们开头提到的web中手动转码的问题非常相像。但是,这次为什么失败了呢。先让我们看看上面代码做了什么。相当于1.首先把str通过gbk编码得到了字节数组b2.把字节数组b用uft8字符集解码生成新的字符str23.把str2字符用utf-8字符集编码成字节数组b2(2)(3)看似可逆。实际上是1.首先把str通过gbk编码得到了字节数组b2.利用字节数组b,通过utf-8到unicode的转换算法生成新的字符str23.把字符串str2利用utf-8到unicode的转换算法解码生成字节数组b2为什么b的值与b2的值不同呢?现是字节数组通过utf-8解码生成字符,然后字符通过utf-8编码生成字节数组。明明是可逆的操作,为什么不能还原.让b2与b相等呢?确实这种转换算法实际上是可逆的。但问题出现在哪了呢?其实大家忽略了一个细节我们的b字节数组里面的值是gbk编码形式的。把一个gbk形式的字节流 通过utf-8到unicode转换算法 转成 unicode码。这个过程之中因为utf-8与gbk的不兼容。导致了转换过程出现了错误。所以在,你想转回来的时候,你用错误的结果无法转换回来。再来看看最开头提到的例子:public class Test4 { public static void main(String[] args) throws Exception { String str = "天"; byte[] b = str.getBytes("gbk"); show(b); String str2 = new String(b,"ISO8859-1"); byte[] b2 = str2.getBytes("ISO8859-1"); show(b2); System.out.println(new String(str2.getBytes("ISO8859-1"),"gbk")); } public static void show(byte[] b){ for (int i = 0; i < b.length; i++){ System.out.print(b[i]+ " "); } System.out.println(); } }
结果如下:-52 -20
-52 -20
天为什么用ISO8859-1就成功了呢?为什么用ISO8859-1就可逆了呢?如果详细说会比较麻烦,你可以认为在这种操作的时候ISO8859-1和gbk的兼容性比较好。如果你觉得只说兼容性这个词太抽象的话,可以稍微这么理解。b[]字节数组里面存的是gbk编码,如果用utf-8到unicode的转换算法。这个字节数组里面的某些位用这种算法无法解析,于是数据就出现了错误,数据错误之后,你想转换回来当然不行了(所谓的兼容性不好)。b[]字节数组里面存的是gbk编码,如果用iso8859-1到unicode的转换算法。虽然字节数组的某些位不是标准的iso8859-1的格式,但是这种算法仍然能按照某种方式解析,所以数据没出现错误,所以你想转换回来的时候,还能转换回来(所谓的兼容性好)。所以说,手工转码不是万能的。
字符编码问题之手动转码并不万能
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.