小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

java中文GBK和UTF-8編碼轉(zhuǎn)換亂碼的分析

 Levy_X 2017-10-11

原文:http://blog.csdn.net/54powerman/article/details/77575656

作者:54powerman

一直以為,java中任意unicode字符串,可以使用任意字符集轉(zhuǎn)為byte[]再轉(zhuǎn)回來(lái),只要不拋出異常就不會(huì)丟失數(shù)據(jù),事實(shí)證明這是錯(cuò)的。

經(jīng)過(guò)這個(gè)實(shí)例,也明白了為什么 getBytes()需要捕獲異常,雖然有時(shí)候它也沒(méi)有捕獲到異常。

言歸正傳,先看一個(gè)實(shí)例。

用ISO-8859-1中轉(zhuǎn)UTF-8數(shù)據(jù)

設(shè)想一個(gè)場(chǎng)景:

用戶A,有一個(gè)UTF-8編碼的字節(jié)流,通過(guò)一個(gè)接口傳遞給用戶B;

用戶B并不知道是什么字符集,他用ISO-8859-1來(lái)接收,保存;

在一定的處理流程處理后,把這個(gè)字節(jié)流交給用戶C或者交還給用戶A,他們都知道這是UTF-8,他們解碼得到的數(shù)據(jù),不會(huì)丟失。

下面代碼驗(yàn)證:

01public static void main(String[] args) throws Exception {
02  //這是一個(gè)unicode字符串,與字符集無(wú)關(guān)
03  String str1 = '用戶';
04
05  System.out.println('unicode字符串:' str1);
06
07  //將str轉(zhuǎn)為UTF-8字節(jié)流
08  byte[] byteArray1=str1.getBytes('UTF-8');//這個(gè)很安全,UTF-8不會(huì)造成數(shù)據(jù)丟失
09
10  System.out.println(byteArray1.length);//打印6,沒(méi)毛病
11
12  //下面交給另外一個(gè)人,他不知道這是UTF-8字節(jié)流,因此他當(dāng)做ISO-8859-1處理
13
14  //將byteArray1當(dāng)做一個(gè)普通的字節(jié)流,按照ISO-8859-1解碼為一個(gè)unicode字符串
15  String str2=new String(byteArray1,'ISO-8859-1');
16
17  System.out.println('轉(zhuǎn)成ISO-8859-1會(huì)亂碼:' str2);
18
19  //將ISO-8859-1編碼的unicode字符串轉(zhuǎn)回為byte[]
20  byte[] byteArray2=str2.getBytes('ISO-8859-1');//不會(huì)丟失數(shù)據(jù)
21
22  //將字節(jié)流重新交回給用戶A
23
24  //重新用UTF-8解碼
25  String str3=new String(byteArray2,'UTF-8');
26
27  System.out.println('數(shù)據(jù)沒(méi)有丟失:' str3);
28}

輸出:

1unicode字符串:用戶
26
3轉(zhuǎn)成ISO-8859-1會(huì)亂碼:?”¨??·
4數(shù)據(jù)沒(méi)有丟失:用戶

用GBK中轉(zhuǎn)UTF-8數(shù)據(jù)

重復(fù)前面的流程,將ISO-8859-1 用GBK替換。

只把中間一段改掉:

1//將byteArray1當(dāng)做一個(gè)普通的字節(jié)流,按照GBK解碼為一個(gè)unicode字符串
2    String str2=new String(byteArray1,'GBK');
3
4    System.out.println('轉(zhuǎn)成GBK會(huì)亂碼:' str2);
5
6    //將GBK編碼的unicode字符串轉(zhuǎn)回為byte[]
7    byte[] byteArray2=str2.getBytes('GBK');//數(shù)據(jù)會(huì)不會(huì)丟失呢?

運(yùn)行結(jié)果:

1unicode字符串:用戶
26
3轉(zhuǎn)成GBK會(huì)亂碼:鐢ㄦ埛
4數(shù)據(jù)沒(méi)有丟失:用戶

好像沒(méi)有問(wèn)題,這就是一個(gè)誤區(qū)。

修改原文字符串重新測(cè)試

將兩個(gè)漢字 “用戶” 修改為三個(gè)漢字 “用戶名” 重新測(cè)試。

ISO-8859-1測(cè)試結(jié)果:

1unicode字符串:用戶名
29
3轉(zhuǎn)成GBK會(huì)亂碼:?”¨??·???
4數(shù)據(jù)沒(méi)有丟失:用戶名

GBK 測(cè)試結(jié)果:

1unicode字符串:用戶名
29
3轉(zhuǎn)成GBK會(huì)亂碼:鐢ㄦ埛鍚?
4數(shù)據(jù)沒(méi)有丟失:用戶??

結(jié)論出來(lái)了

ISO-8859-1 可以作為中間編碼,不會(huì)導(dǎo)致數(shù)據(jù)丟失;

GBK 如果漢字?jǐn)?shù)量為偶數(shù),不會(huì)丟失數(shù)據(jù),如果漢字?jǐn)?shù)量為奇數(shù),必定會(huì)丟失數(shù)據(jù)。

why?

為什么奇數(shù)個(gè)漢字GBK會(huì)出錯(cuò)

直接對(duì)比兩種字符集和奇偶字?jǐn)?shù)的情形

重新封裝一下前面的邏輯,寫(xiě)一段代碼來(lái)分析:

01public static void demo(String str) throws Exception {
02  System.out.println('原文:'  str);
03
04  byte[] utfByte = str.getBytes('UTF-8');
05  System.out.print('utf Byte:');
06  printHex(utfByte);
07  String gbk = new String(utfByte, 'GBK');//這里實(shí)際上把數(shù)據(jù)破壞了
08  System.out.println('to GBK:'  gbk);
09
10  byte[] gbkByte=gbk.getBytes('GBK');
11  String utf = new String(gbkByte, 'UTF-8');
12  System.out.print('gbk Byte:');
13  printHex(gbkByte);
14  System.out.println('revert UTF8:'  utf);
15  System.out.println('===');
16//      如果gbk變成iso-8859-1就沒(méi)問(wèn)題
17}
18
19public static void printHex(byte[] byteArray) {
20  StringBuffer sb = new StringBuffer();
21  for (byte b : byteArray) {
22    sb.append(Integer.toHexString((b >> 4) & 0xF));
23    sb.append(Integer.toHexString(b & 0xF));
24    sb.append(' ');
25  }
26  System.out.println(sb.toString());
27};
28
29public static void main(String[] args) throws Exception {
30  String str1 = '姓名';
31  String str2 = '用戶名';
32  demo(str1,'UTF-8','ISO-8859-1');
33  demo(str2,'UTF-8','ISO-8859-1');
34
35  demo(str1,'UTF-8','GBK');
36  demo(str2,'UTF-8','GBK');
37}

輸出結(jié)果:

01原文:姓名
02UTF-8 Byte:e5 a7 93 e5 90 8d
03to ISO-8859-1:?§“???
04ISO-8859-1 Byte:e5 a7 93 e5 90 8d
05revert UTF-8:姓名
06===
07原文:用戶名
08UTF-8 Byte:e7 94 a8 e6 88 b7 e5 90 8d
09to ISO-8859-1:?”¨??·???
10ISO-8859-1 Byte:e7 94 a8 e6 88 b7 e5 90 8d
11revert UTF-8:用戶名
12===
13原文:姓名
14UTF-8 Byte:e5 a7 93 e5 90 8d
15to GBK:濮撳悕
16GBK Byte:e5 a7 93 e5 90 8d
17revert UTF-8:姓名
18===
19原文:用戶名
20UTF-8 Byte:e7 94 a8 e6 88 b7 e5 90 8d
21to GBK:鐢ㄦ埛鍚?
22GBK Byte:e7 94 a8 e6 88 b7 e5 90 3f
23revert UTF-8:用戶??
24===

為什么GBK會(huì)出錯(cuò)

前三段都沒(méi)問(wèn)題,最后一段,奇數(shù)個(gè)漢字的utf-8字節(jié)流轉(zhuǎn)成GBK字符串,再轉(zhuǎn)回來(lái),前面一切正常,最后一個(gè)字節(jié),變成了 “0x3f”,即”?”

我們使用”用戶名” 三個(gè)字來(lái)分析,它的UTF-8 的字節(jié)流為:

[e7 94 a8] [e6 88 b7] [e5 90 8d]

我們按照三個(gè)字節(jié)一組分組,他被用戶A當(dāng)做一個(gè)整體交給用戶B。

用戶B由于不知道是什么字符集,他當(dāng)做GBK處理,因?yàn)镚BK是雙字節(jié)編碼,如下按照兩兩一組進(jìn)行分組:

[e7 94] [a8 e6] [88 b7] [e5 90] [8d ?]

不夠了,怎么辦?它把 0x8d當(dāng)做一個(gè)未知字符,用一個(gè)半角Ascii字符的 “?” 代替,變成了:

[e7 94] [a8 e6] [88 b7] [e5 90] 3f

數(shù)據(jù)被破壞了。

為什么 ISO-8859-1 沒(méi)問(wèn)題

因?yàn)?ISO-8859-1 是單字節(jié)編碼,因此它的分組方案是:

[e7] [94] [a8] [e6] [88] [b7] [e5] [90] [8d]

因此中間不做任何操作,交回個(gè)用戶A的時(shí)候,數(shù)據(jù)沒(méi)有變化。

關(guān)于Unicode編碼

因?yàn)閁TF-16 區(qū)分大小端,嚴(yán)格講:unicode==UTF16BE。

view sourceprint?

1public static void main(String[] args) throws Exception {
2  String str='測(cè)試';
3  printHex(str.getBytes('UNICODE'));
4  printHex(str.getBytes('UTF-16LE'));
5  printHex(str.getBytes('UTF-16BE'));
6}

運(yùn)行結(jié)果:

1fe ff 6d 4b 8b d5
24b 6d d5 8b
36d 4b 8b d5

其中 “fe ff” 為大端消息頭,同理,小端消息頭為 “ff fe”。

小結(jié)

作為中間轉(zhuǎn)存方案,ISO-8859-1 是安全的。

UTF-8 字節(jié)流,用GBK字符集中轉(zhuǎn)是不安全的;反過(guò)來(lái)也是同樣的道理。

01byte[] utfByte = str.getBytes('UTF-8');
02String gbk = new String(utfByte, 'GBK');
03這是錯(cuò)誤的用法,雖然在ISO-8859-1時(shí)并沒(méi)報(bào)錯(cuò)。
04
05首先,byte[] utfByte = str.getBytes('UTF-8');
06執(zhí)行完成之后,utfByte 已經(jīng)很明確,這是utf-8格式的字節(jié)流;
07
08然后,gbk = new String(utfByte, 'GBK'),
09對(duì)utf-8的字節(jié)流使用gbk解碼,這是不合規(guī)矩的。
10
11就好比一個(gè)美國(guó)人說(shuō)一段英語(yǔ),讓一個(gè)不懂英文又不會(huì)學(xué)舌的日本人聽(tīng),然后傳遞消息給另一個(gè)美國(guó)人。
12
13為什么ISO-8859-1 沒(méi)問(wèn)題呢?
14
15因?yàn)樗徽J(rèn)識(shí)一個(gè)一個(gè)的字節(jié),就相當(dāng)于是一個(gè)錄音機(jī)。我管你說(shuō)的什么鬼話連篇,過(guò)去直接播放就可以了。

getBytes() 是會(huì)丟失數(shù)據(jù)的操作,而且不一定會(huì)拋異常。

unicode是安全的,因?yàn)樗莏ava使用的標(biāo)準(zhǔn)類型,跨平臺(tái)無(wú)差異。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多