一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

java中文GBK和UTF-8編碼轉換亂碼的分析

 Levy_X 2017-10-11

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

作者:54powerman

一直以為,java中任意unicode字符串,可以使用任意字符集轉為byte[]再轉回來,只要不拋出異常就不會丟失數據,事實證明這是錯的。

經過這個實例,也明白了為什么 getBytes()需要捕獲異常,雖然有時候它也沒有捕獲到異常。

言歸正傳,先看一個實例。

用ISO-8859-1中轉UTF-8數據

設想一個場景:

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

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

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

下面代碼驗證:

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

輸出:

1unicode字符串:用戶
26
3轉成ISO-8859-1會亂碼:?”¨??·
4數據沒有丟失:用戶

用GBK中轉UTF-8數據

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

只把中間一段改掉:

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

運行結果:

1unicode字符串:用戶
26
3轉成GBK會亂碼:鐢ㄦ埛
4數據沒有丟失:用戶

好像沒有問題,這就是一個誤區(qū)。

修改原文字符串重新測試

將兩個漢字 “用戶” 修改為三個漢字 “用戶名” 重新測試。

ISO-8859-1測試結果:

1unicode字符串:用戶名
29
3轉成GBK會亂碼:?”¨??·???
4數據沒有丟失:用戶名

GBK 測試結果:

1unicode字符串:用戶名
29
3轉成GBK會亂碼:鐢ㄦ埛鍚?
4數據沒有丟失:用戶??

結論出來了

ISO-8859-1 可以作為中間編碼,不會導致數據丟失;

GBK 如果漢字數量為偶數,不會丟失數據,如果漢字數量為奇數,必定會丟失數據。

why?

為什么奇數個漢字GBK會出錯

直接對比兩種字符集和奇偶字數的情形

重新封裝一下前面的邏輯,寫一段代碼來分析:

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');//這里實際上把數據破壞了
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就沒問題
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}

輸出結果:

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會出錯

前三段都沒問題,最后一段,奇數個漢字的utf-8字節(jié)流轉成GBK字符串,再轉回來,前面一切正常,最后一個字節(jié),變成了 “0x3f”,即”?”

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

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

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

用戶B由于不知道是什么字符集,他當做GBK處理,因為GBK是雙字節(jié)編碼,如下按照兩兩一組進行分組:

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

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

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

數據被破壞了。

為什么 ISO-8859-1 沒問題

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

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

因此中間不做任何操作,交回個用戶A的時候,數據沒有變化。

關于Unicode編碼

因為UTF-16 區(qū)分大小端,嚴格講:unicode==UTF16BE。

view sourceprint?

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

運行結果:

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

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

小結

作為中間轉存方案,ISO-8859-1 是安全的。

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

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

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

unicode是安全的,因為他是java使用的標準類型,跨平臺無差異。

    本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯系方式、誘導購買等信息,謹防詐騙。如發(fā)現有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    亚洲欧洲日韩综合二区| 国产精品不卡一区二区三区四区| 欧美一级片日韩一级片| 亚洲中文字幕高清视频在线观看| 99久久精品午夜一区二| 日韩欧美二区中文字幕| 日韩偷拍精品一区二区三区| 日韩黄片大全免费在线看| 国产激情一区二区三区不卡| 日韩一区二区免费在线观看| 四季精品人妻av一区二区三区 | 国产在线一区二区三区不卡| 亚洲欧美国产中文色妇| 欧美精品在线观看国产| 精品推荐国产麻豆剧传媒| 一区二区三区18禁看| 欧美日本亚欧在线观看| 国产亚州欧美一区二区| 欧美高潮喷吹一区二区| 日韩精品小视频在线观看| 精品欧美一区二区三久久| 少妇成人精品一区二区| 微拍一区二区三区福利| 最近中文字幕高清中文字幕无| 久草精品视频精品视频精品| 国产精品午夜一区二区三区 | 日本道播放一区二区三区| 亚洲中文字幕亲近伦片| 亚洲av日韩一区二区三区四区 | 亚洲精品av少妇在线观看| 国产又粗又猛又长又大| 国产精品一区二区视频大全| 亚洲欧美日韩熟女第一页| 国产精品免费视频久久| 色偷偷亚洲女人天堂观看| 91精品国产综合久久精品| 国产成人亚洲综合色就色| 国产av熟女一区二区三区四区 | 国产一级一片内射视频在线| 日本加勒比在线播放一区| 日韩欧美国产三级在线观看|