字符編碼問題之手動轉碼並不萬能


最開始接觸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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章