中文亂碼的產生原因是什麼

2022-11-09 14:00:40

中文亂碼的產生原因:解碼方式和編碼方式不一致。一箇中文字元以utf-8編碼會轉成3個byte,如果以gbk編碼會轉成2個byte;而一個英文字元以utf-8編碼會轉成1個byte,如果以gbk編碼會轉成1個byte。

php入門到就業線上直播課:進入學習
Apipost = Postman + Swagger + Mock + Jmeter 超好用的API偵錯工具:

本教學操作環境:windows7系統、Dell G3電腦。

先說一下什麼叫亂碼

不知道有沒有人這樣認為過,一個字串不僅僅包含字元,還有隱藏著它的編碼資訊。比如java中String str = "你好";我之前是這樣認為的,str這個字串隱藏著它的編碼方式unicode編碼或者gbk、iso-8859-1等。這種理解是錯誤的,字元就是字元沒有任何其他資訊,正確的理解應該是,人在一個檔案中所看到的字串是系統經過把記憶體中的數碼資訊讀取也再解碼成一些字元最後顯示,就是當你雙擊開啟一個文字檔案時系統會把記憶體的數碼資訊讀取顯示出來,當你儲存一個文字檔案時系統會把這個檔案以你所設定的編碼方式編碼,再放進記憶體中。

所以說亂碼也是一些字元,只是奇怪的字元而已,並沒有什麼」碼「。

接著說亂碼產生的原因

我們經常看到網上這樣解釋亂碼原因:亂碼是因為解碼方式和編碼方式不一致導致的,這句話本身沒有錯,但同樣這句話的本身就是把亂碼概括了而已,它並不能幫助你理解亂碼。

所以我們要提的問題是:為什麼解碼方式和編碼方式不一致會出現亂碼。

這裡以utf-8,gbk,iso-8859-1三種編碼方式為例。

     @Test
     public void testEncode() throws Exception {
        String str = "你好",en = "h?h";
        
        System.out.println("========中文字元utf-8=======");
        byte[] utf8 = str.getBytes(); // 以utf-8方式編碼 ,default:utf-8
        for (byte b : utf8) {            
            System.out.print(b + "\t");
        }
        
        System.out.println("\n"+"========英文字元utf-8=======");
        byte[] utf8_en = en.getBytes(); // 以utf-8方式編碼 ,default:utf-8
        for (byte b : utf8_en) {            
            System.out.print(b + "\t");
        }
        
        System.out.println("\n"+"========中文字元gbk=========");
        byte[] gbk = str.getBytes("gbk");
        for (byte b : gbk) {            
            System.out.print(b + "\t");
        }
        
        System.out.println("\n"+"========英文字元gbk=========");
        byte[] gbk_en = en.getBytes("gbk");
        for (byte b : gbk_en) {            
            System.out.print(b + "\t");
        }
        
        String s = new String(utf8,"utf-8");
        String s1 = new String(utf8,"gbk");
        System.out.println("\n"+s + "====gbk:" + s1);
     }
登入後複製

測試上面方法,列印的結果是:

========中文字元utf-8=======
-28 -67  -96 -27  -91 -67  
========英文字元utf-8=======
104 63  104 
========中文字元gbk=========
-60 -29  -70 -61  
========英文字元gbk=========
104 63  104 
你好====gbk:浣犲ソ
------------------------------------------------------------------------------------
登入後複製

可以得出結論:

一箇中文字元以utf-8編碼會轉成3個byte,如果以gbk編碼會轉成2個byte;

一個英文字元以utf-8編碼會轉成1個byte,如果以gbk編碼會轉成1個byte。

從列印的最後一行結合29-31行程式碼可以看出,如果把byte陣列utf8 以utf-8的方式解碼不會有亂碼,還是原來的」你好「,而如果以gbk方式解碼則出現了三個亂碼字元,為什麼是3個而不是2個呢,6/2=3。

接下來說iso-8859-1,這種編碼應用於英文系列,也就是說不能表示中文(如果要使用必須依賴於其它相容iso-8859-1編碼方式的編碼),它讀不懂的字元都將被視為英文問號'?',英文問號的iso-8859-1編碼號是:63(十進位制)(其實在幾乎所有的編碼方式中,所有英文字元都用1個固定的位元組碼錶示,unicode編碼除外)。

     @Test
     public void testISO() throws Exception {
         String str = "你好";
         byte[] bs = str.getBytes("iso-8859-1");
         for (byte b : bs) {
            System.out.println(b);
         }
         System.out.println(new String(bs,"iso-8859-1"));
         System.out.println(new String(bs,"utf-8"));
         System.out.println(new String(bs,"gbk"));
         System.out.println(new String(bs,"unicode"));         
     }
登入後複製

列印結果

63
63
??
??
??
㼿
登入後複製

說明63 =》?,所有中文都被認為是?,所以說當執行這句程式碼時:byte[] bs = "你好".getBytes("iso-8859-1");資訊已丟失。

再執行String str = new String(bs,"任何charset");str已經不等於"你好"了,而是兩個問號??。所以在tomcat中我們會經常遇上中文變為一長串??????,就是源於此。

在iso-8859-1、utf-8、gbk中一個位元組碼錶示一個英文字元,

在unicode編碼中一個位元組碼並不能表示任何字元,而且規定必須是兩個位元組碼(有時4個)才能表示一個字元。

說了這麼多,也許很多人會問為什麼要用這麼多編碼方式,統一成utf-8不就能表示所有字元了?

編碼不僅僅是要考慮是否能表示任何字元,還要考慮傳輸和儲存。

1、utf-8確實幾乎能表示所有已知字元。前面說過在utf-8編碼中3個位元組才表示一箇中文字元,這樣顯然佔空間,不利於傳輸和儲存(傳輸和儲存都是以二進位制的方式進行的)

2、無疑一個位元組表示一個字元最省空間,比如iso-8859-1。但這世上不是隻有英文字元,還有各個地區國家的文字。所以字元的數量肯定是大於2的8次方的。

所以結合以上兩點,就自然地出現了很多種編碼方式。

瞭解各種編碼方式的規則:https://jingyan.baidu.com/article/020278118741e91bcd9ce566.html

更多程式設計相關知識,請存取:!!

以上就是中文亂碼的產生原因是什麼的詳細內容,更多請關注TW511.COM其它相關文章!