乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      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)回來,只要不拋出異常就不會丟失數(shù)據(jù),事實證明這是錯的。

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

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

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

      設想一個場景:

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

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

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

      下面代碼驗證:

      01public static void main(String[] args) throws Exception {
      02  //這是一個unicode字符串,與字符集無關(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');//這個很安全,UTF-8不會造成數(shù)據(jù)丟失
      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('轉(zhuǎn)成ISO-8859-1會亂碼:' str2);
      18
      19  //將ISO-8859-1編碼的unicode字符串轉(zhuǎn)回為byte[]
      20  byte[] byteArray2=str2.getBytes('ISO-8859-1');//不會丟失數(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ù)沒有丟失:' str3);
      28}

      輸出:

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

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

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

      只把中間一段改掉:

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

      運行結(jié)果:

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

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

      修改原文字符串重新測試

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

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

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

      GBK 測試結(jié)果:

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

      結(jié)論出來了

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

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

      why?

      為什么奇數(shù)個漢字GBK會出錯

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

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

      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ù)據(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就沒問題
      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會出錯

      前三段都沒問題,最后一段,奇數(shù)個漢字的utf-8字節(jié)流轉(zhuǎn)成GBK字符串,再轉(zhuǎn)回來,前面一切正常,最后一個字節(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

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

      為什么 ISO-8859-1 沒問題

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

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

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

      關(guān)于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}

      運行結(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)是不安全的;反過來也是同樣的道理。

      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 已經(jīng)很明確,這是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() 是會丟失數(shù)據(jù)的操作,而且不一定會拋異常。

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

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

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多