日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

再谈java乱码:GBK和UTF-8互转尾部乱码问题分析

發(fā)布時(shí)間:2025/3/21 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 再谈java乱码:GBK和UTF-8互转尾部乱码问题分析 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一直以為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)景

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

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

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

下面代碼驗(yàn)證

public static void main(String[] args) throws Exception {//這是一個(gè)unicode字符串與字符集無(wú)關(guān)String str1 = "用戶(hù)";System.out.println("unicode字符串"+str1);//將str轉(zhuǎn)為UTF-8字節(jié)流byte[] byteArray1=str1.getBytes("UTF-8");//這個(gè)很安全UTF-8不會(huì)造成數(shù)據(jù)丟失System.out.println(byteArray1.length);//打印6沒(méi)毛病//下面交給另外一個(gè)人他不知道這是UTF-8字節(jié)流因此他當(dāng)做ISO-8859-1處理//將byteArray1當(dāng)做一個(gè)普通的字節(jié)流按照ISO-8859-1解碼為一個(gè)unicode字符串String str2=new String(byteArray1,"ISO-8859-1");System.out.println("轉(zhuǎn)成ISO-8859-1會(huì)亂碼"+str2);//將ISO-8859-1編碼的unicode字符串轉(zhuǎn)回為byte[]byte[] byteArray2=str2.getBytes("ISO-8859-1");//不會(huì)丟失數(shù)據(jù)//將字節(jié)流重新交回給用戶(hù)A//重新用UTF-8解碼String str3=new String(byteArray2,"UTF-8");System.out.println("數(shù)據(jù)沒(méi)有丟失"+str3); }

輸出

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

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

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

只把中間一段改掉

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

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

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

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

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

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

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

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

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

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

結(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)分析

public static void demo(String str) throws Exception {System.out.println("原文" + str);byte[] utfByte = str.getBytes("UTF-8");System.out.print("utf Byte");printHex(utfByte);String gbk = new String(utfByte, "GBK");//這里實(shí)際上把數(shù)據(jù)破壞了System.out.println("to GBK" + gbk);byte[] gbkByte=gbk.getBytes("GBK");String utf = new String(gbkByte, "UTF-8");System.out.print("gbk Byte");printHex(gbkByte);System.out.println("revert UTF8" + utf);System.out.println("==="); // 如果gbk變成iso-8859-1就沒(méi)問(wèn)題 }public static void printHex(byte[] byteArray) {StringBuffer sb = new StringBuffer();for (byte b : byteArray) {sb.append(Integer.toHexString((b >> 4) & 0xF));sb.append(Integer.toHexString(b & 0xF));sb.append(" ");}System.out.println(sb.toString()); };public static void main(String[] args) throws Exception {String str1 = "姓名";String str2 = "用戶(hù)名";demo(str1,"UTF-8","ISO-8859-1");demo(str2,"UTF-8","ISO-8859-1");demo(str1,"UTF-8","GBK");demo(str2,"UTF-8","GBK"); }

輸出結(jié)果

原文姓名 UTF-8 Bytee5 a7 93 e5 90 8d to ISO-8859-1:?§“? ISO-8859-1 Bytee5 a7 93 e5 90 8d revert UTF-8姓名 === 原文用戶(hù)名 UTF-8 Bytee7 94 a8 e6 88 b7 e5 90 8d to ISO-8859-1:?”¨??·? ISO-8859-1 Bytee7 94 a8 e6 88 b7 e5 90 8d revert UTF-8用戶(hù)名 === 原文姓名 UTF-8 Bytee5 a7 93 e5 90 8d to GBK:濮撳悕 GBK Bytee5 a7 93 e5 90 8d revert UTF-8姓名 === 原文用戶(hù)名 UTF-8 Bytee7 94 a8 e6 88 b7 e5 90 8d to GBK:鐢ㄦ埛鍚 GBK Bytee7 94 a8 e6 88 b7 e5 90 3f revert UTF-8用戶(hù)? ===

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

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

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

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

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

用戶(hù)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è)用戶(hù)A的時(shí)候數(shù)據(jù)沒(méi)有變化。

關(guān)于Unicode編碼

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

public static void main(String[] args) throws Exception {String str="測(cè)試";printHex(str.getBytes("UNICODE"));printHex(str.getBytes("UTF-16LE"));printHex(str.getBytes("UTF-16BE")); }

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

fe ff 6d 4b 8b d5 4b 6d d5 8b 6d 4b 8b d5

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

小結(jié)

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

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

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

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

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

總結(jié)

以上是生活随笔為你收集整理的再谈java乱码:GBK和UTF-8互转尾部乱码问题分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。