中文亂碼的產生原因:解碼方式和編碼方式不一致。一個中文字元以utf-8編碼會轉成3個byte,如果以gbk編碼會轉成2個byte;而一個英文字符以utf-8編碼會轉成1個byte,如果以gbk編碼會轉成1個byte。
本教學操作環境: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
更多程式相關知識,請造訪:程式設計教學! !
以上是中文亂碼的產生原因是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!