字符编码问题之手动转码并不万能


最开始接触web的时候,因为Tomcat服务器默认的URIEncoding是ISO8859-1。
应该产生过中文乱码问题,某些前辈就会告诉你,可以手动转码。
new String(str.getBytes("IOS8859-1"),"gbk");  
如果页面编码方式是gbk,Tomcat URIEncoding是ISO8859-1。
这样编码再解码,看着确实合理,而且能解决问题。
但是其实手动转码局限性是比较大的。
 
基本概念介绍
     编码是干什么
               编码就是把字符按照规定的字符集转换成 0 1 的字节流形式.
     解码是什么
             是把相应的0 1字节流按照规定字符集转换成相应字符的形式。
Java中的字符都是按照Unicode字符集进行编码的。
           也就是说,JVM管理的内存中的,字符都是以Unicode编码形式存在的。
而非JVM的内存中,字符是以各种各样的字符集进行编码的,当Java程序与外界交互的时候(如I/O),就会涉及到编码解码问题。
在Java中,不同的字符集都是以Unicode作为桥梁相互转换的。
  • String两个常用API原理介绍。
 
public 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();  
   }  

结果:-52 -20
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编码得到了字节数组b
2.把字节数组b用uft8字符集解码生成新的字符str2
3.把str2字符用utf-8字符集编码成字节数组b2

(2)(3)看似可逆。

实际上是

1.首先把str通过gbk编码得到了字节数组b
2.利用字节数组b,通过utf-8到unicode的转换算法生成新的字符str2
3.把字符串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的格式,但是这种算法仍然能按照某种方式解析,所以数据没出现错误,所以你想转换回来的时候,还能转换回来(所谓的兼容性好)。
所以说,手工转码不是万能的。

发布了51 篇原创文章 · 获赞 1 · 访问量 4万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章