亂碼問題經(jīng)常碰到,而且每次都要花很久時(shí)間才能解決。最近想搞清楚一些實(shí)質(zhì)性的東西,看了一些關(guān)于這方面的資料,在這里做個(gè)記錄
造成字符亂碼問題的原因有兩個(gè):
1是編碼很解碼的方式不一致,
2是采用了錯(cuò)誤的編碼造成數(shù)據(jù)丟失。
由于計(jì)算機(jī)的最小存儲(chǔ)單元是字節(jié)即8位二進(jìn)制數(shù),中文是無法在內(nèi)存中直接表示的,所以從字符到字節(jié)就需要通過某個(gè)規(guī)則轉(zhuǎn)換即編碼
編碼方式有很多 我比較常用的是utf-8 GBK iso-8859-1和ASCII,這些都可以看成是字典表。
比如我用utf-8的方式去編碼字符串"你好"
String str = "你好";
byte[] utf8 = str.getBytes("utf-8");
for(byte b : utf8){
System.out.print(" "+b);
}
輸出的結(jié)果是: -28 -67 -96 -27 -91 -67
說明“你好”這個(gè)字符串被編碼成了 6個(gè)字節(jié),utf-8是用三個(gè)字節(jié)表示一個(gè)中文字符的,這種轉(zhuǎn)換過程相當(dāng)于我們查字典
如果用GBK編碼
String str = "你好";
byte[] gbk = str.getBytes("GBK");
for(byte b : gbk){
System.out.print(" "+b);
}
得到的結(jié)果是: -60 -29 -70 -61 GBK是用兩個(gè)字節(jié)表示一個(gè)中文字符的(需要注意getBytes()這個(gè)方法有兩種形式 一個(gè)是帶參數(shù)一個(gè)不帶參數(shù),
不帶參用系統(tǒng)默認(rèn)編碼,中文windows操作系統(tǒng)一般都是GBK 一種查看系統(tǒng)編碼的方式是用命令行chcp 936表示GBK)
到這里我們就可以解釋我們的第一個(gè)原因
比如我們用從網(wǎng)絡(luò)中去抓取數(shù)據(jù)的時(shí)候,對(duì)方用GBK方式進(jìn)行編碼,我們拿到字節(jié)流的時(shí)候用utf-8進(jìn)行解碼,
百度用的是GBK編碼
URL url = new URL("http://www.baidu.com/");
URLConnection connection = url.openConnection();
InputStream inputStream = connection.getInputStream();
InputStreamReader reader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(reader);
System.out.println("bufferedReader = " + bufferedReader.readLine());
得到的結(jié)果是:
bufferedReader = <!doctype html><html><head><meta http-equiv="Content-Type" content="text/html;charset=gb2312"><title>????????????? 。。。。
如果是GBK解碼字節(jié)流得到如下的結(jié)果
<!doctype html><html><head><meta http-equiv="Content-Type" content="text/html;charset=gb2312"><title>百度一下,你就知道 。。。。
由于編解碼方式不一致,utf8去解碼字節(jié)流的時(shí)候從“字典”中查不到相應(yīng)的字符,
默認(rèn)(有幾種 “?"也是我們經(jīng)常見到的一種)用U+003F代替 即 “?”
這里有個(gè)問題我還沒有搞清楚,我用GBK編碼”你好“再用utf-8解碼得到的是3個(gè)”?“ 為什么是3個(gè)"?"
如果了解utf-8 GBK和iso-8859-1等這些的具體編碼方式,第二個(gè)問題就比較清楚了。
比如iso-8859-1是單字節(jié)編碼的,比起ASSCII,iso-8859-1把一個(gè)字節(jié)的最高位也納入進(jìn)來表示字符,但是最多也只有256個(gè)字符,
實(shí)際上它是在ASSCII基礎(chǔ)之上加入了一些西歐字符,并沒有加入中文,所以iso-8859-1也不能表示中文,如果我們這么寫
String str = "你好";
byte[] bytes = str.getBytes("iso-8859-1");
System.out.println("bytes = " + new String(bytes,"utf-8")); /////輸出 ??
System.out.println("bytes = " + new String(bytes,"GBK")); /////輸出 ??
System.out.println("bytes = " + new String(bytes,"iso-8859-1")); /////輸出 ??
由于在“字典”中找不到相應(yīng)的值,就用3f替代 我們用上面的方式打印 得到的都會(huì)是"??",
java用的是Unicode編碼,API中Character類下有描述:“字符信息基于 Unicode 標(biāo)準(zhǔn),版本 4.0。。。”
更實(shí)際的Web編程中遇到的瀏覽器顯示亂碼和應(yīng)用程序接收提交過去的亂碼問題以下博客中都有詳細(xì)的描述,