色婷婷狠狠18禁久久YY,CHINESE性内射高清国产,国产女人18毛片水真多1,国产AV在线观看

為什么改了gbk還是亂碼

傅智翔2年前22瀏覽0評論

為什么改了gbk還是亂碼?

我們知道在計算機內存中,存儲的是二進制數據,在網絡傳輸中,也是二進制數據,但最終呈現給用戶的是字符串,二進制與字符串的轉化就需要編碼、解碼的參與,如果世界上只有一種字符編碼方式,就不會有亂碼這一說了,但事實是,編碼的方式太多了,utf-8、utf-32、utf-16、GBK、gb2312、iso-8859-1、big5、unicode等等。由于每個編碼的規則不一樣,一般都不能用一種進行編碼,用另一種進行解碼。如utf-8中,一個字母用一個字節表示,一個漢字用三個字節表示,特殊的漢字用四個字節表示,而gbk中,一個字母用一個字節表示,一個漢字用兩個字節表示。

有一個說法,內存中存儲的二進制是unicode碼,如果內存中的數據需要存儲或傳輸時,才會進行一次轉化,將unicode碼轉化成其它的編碼二進制(有待考證)。個人覺得這種方式很合理,畢竟unicode碼中每個字符都有獨一無二的二進制與之對應。

排查亂碼問題,難度在于是在哪個環節出了問題,但亂碼的本質都是一樣的,讀取二進制的編碼和最初將字符串轉化成二進制的編碼方式不一致。

此處說明一個概念,編碼指將字符串轉化成二進制,解碼指將二進制轉化成字符串。

UTF-8編碼,GBK解碼

在這我們討論一下,gbk和utf-8互轉的亂碼問題,直接上代碼。

package com.anjz.test;

import java.io.UnsupportedEncodingException;

public class CodingTest {

public static void main(String[] args) throws UnsupportedEncodingException {

String str = "你好,世界";

System.out.println("字符串長度:"+str.length());

byte[] utfBytes = str.getBytes("utf-8");

System.out.println("UTF-8需要"+utfBytes.length+"字節存儲");

byte[] gbkBytes = str.getBytes("gbk");

System.out.println("gbk需要"+gbkBytes.length+"字節存儲");

}

}

以上代碼運行打印出一下內容:

字符串長度:5

utf-8需要15字節存儲

gbk需要10字節存儲

可以看出,utf-8存儲一個漢字,需要3個字節,gbk存儲一個漢字,需要2個字節。

現用單個字符測試。

package com.anjz.test;

import java.io.UnsupportedEncodingException;

public class CodingTest {

public static void main(String[] args) throws UnsupportedEncodingException {

String str = "你";

byte[] utfBytes = str.getBytes("utf-8");

for(byte utfByte:utfBytes){

//字節對應的十進制是負數,因java中的二進制使用補碼表示的,此處使用0xff 還原成int表示的數據,再轉化成16進制

System.out.print(Integer.toHexString((utfByte & 0xFF)) +",");

}

System.out.println();

String utf2gbkStr = new String(str.getBytes("utf-8"),"gbk");

System.out.println("utf-8轉化成gbk:"+utf2gbkStr);

byte[] gbkBytes = utf2gbkStr.getBytes("gbk");

for(byte gbkByte:gbkBytes){

System.out.print(Integer.toHexString((gbkByte & 0xFF))+",");

}

System.out.println();

String gbk2utfStr = new String(utf2gbkStr.getBytes("gbk"),"utf-8");

System.out.println("gbk轉化成utf-8:"+gbk2utfStr);

}

}

運行上面代碼,得出的結果:

e4,bd,a0,

utf-8轉化成gbk:浣?

e4,bd,3f,

gbk轉化成utf-8:??

用兩個字符測試,將上述代碼String str = “你”改成String str = “你好”。運行代碼,得出的結果:

e4,bd,a0,e5,a5,bd,

utf-8轉化成gbk:浣犲ソ

e4,bd,a0,e5,a5,bd,

gbk轉化成utf-8:你好

上述實驗中,utf-8轉化成gbk出現亂碼,這個很好理解,但是再還原回去,gbk轉化成utf-8,單個中文字符依然是亂碼,兩個字符卻能正常顯示,這個到底是怎么回事呢?

經過一番研究,想把這個事說明白,還需要從它們的編碼規則著手。

ISO-8859-1

單字節編碼,向下兼容ASCII,其編碼范圍是0x00-0xFF,0x00-0x7F之間完全和ASCII一致,0x80-0x9F之間是控制字符,0xA0-0xFF之間是文字符號。

GBK

采用單雙字節變長編碼,英文使用單字節編碼,完全兼容ASCII字符編碼,中文部分采用雙字節編碼。雙字節其編碼范圍從8140至FEFE(剔除xx7F)。

單字節:00000000 - 01111111

雙字節:10000001 01000000 - 11111110 11111110 (剔除******** 01111111)

單字節、雙字節的區分通過高字節高位區分,單字節高位為0,雙字節的高字節高位為1。

UTF-8

可變長字符編碼,是unicode碼的具體實現,UTF-8用1到6個字節編碼Unicode字符。

UTF-8編碼規則:如果只有一個字節則其最高二進制位為0;如果是多字節,其第一個字節從最高位開始,連續的二進制位值為1的個數決定了其編碼的字節數,其余各字節均以10開頭。

1字節 0xxxxxxx

2字節 110xxxxx 10xxxxxx

3字節 1110xxxx 10xxxxxx 10xxxxxx

4字節 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

5字節 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

6字節 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

明白上述GBK和UTF-8的編碼規則,我們再分析一下,單個中文字符是亂碼,兩個字符卻能正常顯示的問題。

“你”

UTF-8編碼對應的二進制:11100100 10111101 10100000

將上述二進制通過GBK進行解碼,根據GBK規則,第一個字節高位為1,使用雙字節編碼,

“11100100 10111101”解碼成“浣”,“10100000”對于GBK來說是非法的,就解碼成了一種特殊字符“?”。

看看能不能將“浣?”還原回“你”呢?

GBK編碼對應的二進制:11100100 10111101 00111111

看到上述的二進制,根本不符合UTF-8編碼規則,故用UTF-8進行解碼,是解碼成了一些特殊字符“??”。

對于上述情況可以看出,一個二進制,如果不符合當前的編碼規則,會被解碼成特殊字符,但此特殊字符再進行編碼,是回不到最初的二進制的。

用同樣的方式,分析“你好”為什么最終可以正常顯示。

UTF-8編碼對應的二進制:11100100 10111101 10100000 11100101 10100101 10111101

將上述二進制通過GBK進行編碼,根據GBK規則,使用雙字節編碼,“1100100 10111101”解碼成“浣”,“10100000 11100101”解碼成“犲”,“10100101 10111101”解碼成“ソ”。

看看能不能將“浣犲ソ”還原成“你好”呢?

GBK 編碼對應的二進制:11100100 10111101 10100000 11100101 10100101 10111101

可以看出二進制是可以被還原的,將此二進制通過UTF-8解碼,肯定能變成“你好”。

可以看出,一個字符串,通過UTF-8進行編碼,再通過GBK進行解碼,再將得到的字符串進行GBK編碼,最后將得到的二進制通過UTF-8解碼,能否還原到最初的字符串,在于UTF-8編碼后得到的二進制,是否符合GBK的編碼規則,如果符合,最終就可以還原,如果不符合,就不可還原。

GBK編碼,UTF-8解碼

package com.anjz.test;

import java.io.UnsupportedEncodingException;

public class CodingTest {

public static void main(String[] args) throws UnsupportedEncodingException {

String str = "你好";

byte[] gbkBytes = str.getBytes("gbk");

for(byte gbkByte:gbkBytes){

//字節對應的十進制是負數,因java中的二進制使用補碼表示的,此處使用0xff 還原成int表示的數據,再轉化成16進制

System.out.print(Integer.toHexString((gbkByte & 0xFF)) +",");

}

System.out.println();

String gbk2utfStr = new String(str.getBytes("gbk"),"utf-8");

System.out.println("gbk轉化成utf-8:"+gbk2utfStr);

byte[] utfBytes = gbk2utfStr.getBytes("utf-8");

for(byte utfByte:utfBytes){

System.out.print(Integer.toHexString((utfByte & 0xFF))+",");

}

System.out.println();

String utf2gbkStr = new String(gbk2utfStr.getBytes("utf-8"),"gbk");

System.out.println("utf-8轉化成gbk:"+utf2gbkStr);

}

}

運行上述代碼,結果為:

c4,e3,ba,c3,

gbk轉化成utf-8:???

ef,bf,bd,ef,bf,bd,ef,bf,bd,

utf-8轉化成gbk:錕斤拷錕?

上述結果應該都在意料之中,我們通過上述的方法分析一下。

“你好”GBK編碼的二進制:11000100 11100011 10111010 11000011

GBK編碼的二進制數據,完全匹配不了UTF-8的編碼規則,最終UTF-8只能按如下方式匹配,查看第一個字節,開頭“110”,理論上匹配兩個字節,但看下一個字節,開頭卻不是“10”,最終“11000100”解碼成“?”,看第二個字節開頭是“1110”,理論匹配三個字節,看下個字節符合,以“10”開頭,但下下個字節開頭是“110”,不符合匹配,最終“11100011 10111010”解碼成“?”,同理“11000011”也解碼成“?”,這個符號都是為找不到對應規則隨意匹配的一個特殊字符。

“???”UTF-8編碼的二進制為:11101111 10111111 10111101 11101111 10111111 10111101 11101111 10111111 10111101

這個二進制和原先的二進制不相同,根本轉化不到最初的字符串,按照GBK的編碼規則,“11101111 10111111”編碼成“錕”,“10111101 11101111” 編碼成“斤”,“10111111 10111101”編碼成“拷”,“11101111 10111111”編碼成“錕”,“10111101”不符合GBK規則,編碼成特殊字符“?”。

理論上說,用GBK編碼,UTF-8解碼的字符串是不能還原到最初的字符串的,因UTF-8編碼規則的特殊性,GBK編出的二進制,是很難匹配上的。

總結

理論上說,系統出現亂碼,將亂碼還原到最初的樣子,上述UTF-8編碼,GBK解碼,這個有時是可以還原的,有時是還原不了的,要看UTF-8編碼的二進制是否都能符合GBK的編碼規則,但GBK編碼,UTF-8解碼,這個基本是條不歸路。

但實際中,有一種情況,是100%可以將亂碼還原成最初的字符串。就是任意編碼格式編碼,ISO-8859-1解碼,這個主要因為ISO-8859-1是單字節編碼,而且匹配所有單字節情況,亂碼字符串總是可以還原到最初的二

java 二進制,為什么改了gbk還是亂碼