java中的編碼轉(zhuǎn)換過程(以utf8和gbk為例)
java中的編碼轉(zhuǎn)換(以utf8和gbk為例)
在正常javaweb開發(fā)中經(jīng)常會發(fā)現(xiàn)字符轉(zhuǎn)換的需求,會存在中文字符轉(zhuǎn)換亂碼的現(xiàn)象,如何解決以及其轉(zhuǎn)換原理我至今懵懵懂懂,于是專門寫了個測試代碼進(jìn)行嘗試,總算理清了編碼,先上結(jié)論,總結(jié)如下:
utf8中存放有各種語言編碼,當(dāng)前主流開發(fā)中會使用utf8進(jìn)行編碼解碼,該方式不會產(chǎn)生亂碼,產(chǎn)生亂碼有以下幾種情況
- 1、gbk(中文)、iso-8859-1(無中文)等其他方式進(jìn)行編碼,則只能用其對應(yīng)方式進(jìn)行解碼,否則為亂碼
- 2、使用utf8進(jìn)行編碼用其他方式解碼則會導(dǎo)致亂碼,需進(jìn)行一次轉(zhuǎn)換
- 3、使用無對應(yīng)字符(中文)的字符集(iso-8859-1)編碼會導(dǎo)致亂碼,且無法還原解碼
以下是針對以上情況的代碼測試
1.如何編碼就如何解碼
/** ?* 測試編碼轉(zhuǎn)換 中文 => utf-8 編碼 - 解碼 ?*/ @Test public void test0() { ? ? String test = "測試"; ? ? System.out.println(Arrays.toString(test.getBytes(StandardCharsets.UTF_8)));//[-26, -75, -117, -24, -81, -107] ? ? System.out.println(new String(test.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8));//測試 }
/** ?* 測試編碼轉(zhuǎn)換 中文 => gbk 編碼 - 解碼 ?*/ @Test public void test1() throws UnsupportedEncodingException { ? ? String test = "測試"; ? ? System.out.println(Arrays.toString(test.getBytes("gbk")));//[-78, -30, -54, -44] ? ? System.out.println(new String(test.getBytes("gbk"), "GBK"));//測試 }
utf8編碼 - 錯誤形式解碼
/** ?* 測試編碼轉(zhuǎn)換 中文 => utf-8 編碼- gbk解碼 ?*/ @Test public void test2() throws UnsupportedEncodingException { ? ? String test = "測試"; ? ? System.out.println(Arrays.toString(test.getBytes(StandardCharsets.UTF_8)));//[-26, -75, -117, -24, -81, -107] ? ? System.out.println(new String(test.getBytes(StandardCharsets.UTF_8), "gbk"));//嫻嬭瘯 }
正確做法,按錯誤的解碼形式(gbk)作為中轉(zhuǎn),將其按錯誤形式(gbk)重新還原編碼(utf8-encode),再使用utf8進(jìn)行一次正確解碼(utf8-decode)即可得到原來的字符
/** ?* 測試編碼轉(zhuǎn)換 中文 => utf-8 編碼 - gbk 解碼 ===> gbk 編碼 - utf-8解碼 ?* "測試" => (utf8-encode)[-26, -75, -117, -24, -81, -107] => (gbk-decode)嫻嬭瘯 ?* "嫻嬭瘯" => (utf8-encode)[-26, -75, -117, -24, -81, -107] => (utf8-decode)"測試" ?*/ @Test public void test3() throws UnsupportedEncodingException { ? ? String test = "測試"; ? ? String test_gbk_utf8 = new String(test.getBytes(StandardCharsets.UTF_8), "gbk"); ? ? System.out.println(test_gbk_utf8);//嫻嬭瘯 ? ? String test_utf8_gbk = new String(test_gbk_utf8.getBytes("gbk"), StandardCharsets.UTF_8); ? ? System.out.println(test_utf8_gbk);//測試 }
3.無對應(yīng)字符編碼
@Test ? ? public void test4() throws UnsupportedEncodingException { ? ? ? ? String test = "測試"; ? ? ? ? System.out.println(Arrays.toString(test.getBytes(StandardCharsets.ISO_8859_1)));//[63, 63] ? ? ? ? System.out.println(new String(test.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.ISO_8859_1));//?? ? ? }
該情況下即使使用原先的編碼方式進(jìn)行解碼也無法還原字符了,屬于不可逆的狀態(tài)
java編碼格式的轉(zhuǎn)換以及亂碼恢復(fù)
如何在java中進(jìn)行編碼格式轉(zhuǎn)換
下面這行代碼的含義是: 獲取目標(biāo)字符串str的gbk編碼格式的二進(jìn)制碼,然后將二進(jìn)制碼按照utf8編碼格式重新編碼成字符串,當(dāng)然,下面這種寫法百分百會亂碼,因為編碼格式不一致.
new String(str.getBytes("gbk"),"utf8")
首先什么情況會亂碼
如果要傳輸一個字符串,首先要按照一定的編碼格式將字符串轉(zhuǎn)換成字節(jié)流,當(dāng)字節(jié)流傳輸?shù)浇邮辗降臅r候再將字節(jié)流按照某種編碼格式轉(zhuǎn)換成字符串.亂碼也正是產(chǎn)生在重新轉(zhuǎn)換成字符串的過程中.以下是我對中文亂碼的測試:
? String str="彩虹"; ? ? ? ? String [] a=new String[] {"gbk","unicode","utf8","gb2312"}; ? ? ? ? for (int i=0;i<a.length;i++){ ? ? ? ? ? ? for (int j=0;j<a.length;j++){ ? ? ? ? ? ? ? ? System.out.println("二進(jìn)制格式: ? "+a[i]+"編碼格式: ?"+a[j]); ? ? ? ? ? ? ? ? System.out.println("編碼后的字符串: ?"+new String(str.getBytes(a[i]),a[j])); ? ? ? ? ? ? ? } ? ? ? ? }
二進(jìn)制格式: gbk編碼格式: gbk
編碼后的字符串: 彩虹
二進(jìn)制格式: gbk編碼格式: unicode
編碼后的字符串: ??
二進(jìn)制格式: gbk編碼格式: utf8
編碼后的字符串: ???
二進(jìn)制格式: gbk編碼格式: gb2312
編碼后的字符串: 彩虹
二進(jìn)制格式: unicode編碼格式: gbk
編碼后的字符串: ?_i唝
二進(jìn)制格式: unicode編碼格式: unicode
編碼后的字符串: 彩虹
二進(jìn)制格式: unicode編碼格式: utf8
編碼后的字符串: ??_i?y
二進(jìn)制格式: unicode編碼格式: gb2312
編碼后的字符串: ??_i?y
二進(jìn)制格式: utf8編碼格式: gbk
編碼后的字符串: 褰╄櫣
二進(jìn)制格式: utf8編碼格式: unicode
編碼后的字符串: ?馹
二進(jìn)制格式: utf8編碼格式: utf8
編碼后的字符串: 彩虹
二進(jìn)制格式: utf8編碼格式: gb2312
編碼后的字符串: 褰╄??
二進(jìn)制格式: gb2312編碼格式: gbk
編碼后的字符串: 彩虹
二進(jìn)制格式: gb2312編碼格式: unicode
編碼后的字符串: ??
二進(jìn)制格式: gb2312編碼格式: utf8
編碼后的字符串: ???
二進(jìn)制格式: gb2312編碼格式: gb2312
編碼后的字符串: 彩虹
可以看出,如果二進(jìn)制編碼格式和字符串的編碼格式不同就會引起亂碼.
為什么gbk,gb2312轉(zhuǎn)換沒有亂碼?
gbk和gb2312之間的轉(zhuǎn)換沒有亂碼是因為gbk是gb2312的增強版本,支持更多的漢字編碼,所以如果二進(jìn)制編碼格式是gbk而解碼格式是gb2312,這種情況是有可能出現(xiàn)部分漢字亂碼的.
亂碼的數(shù)據(jù)可以轉(zhuǎn)變回來嗎?
上述結(jié)果中的亂碼其實可以大致分為兩種,一種是復(fù)雜的漢字和圖形組合,一種是"?".
如果希望恢復(fù)的亂碼數(shù)據(jù)中有問號,那么這條數(shù)據(jù)恢復(fù)的可能性就不大了.因為除了"?"的其他亂碼其實都是有自己的編碼規(guī)則的,只要逆向的解碼并按照正確的編碼格式重新編碼就可以恢復(fù).但是"?"除外,因為當(dāng)字節(jié)流按照某種編碼格式重新編譯的時候,字節(jié)數(shù)據(jù)中無法按照該編碼格式轉(zhuǎn)換成有意義字符的字節(jié)都會轉(zhuǎn)換成"?",所以就算逆向的編碼成字節(jié)流,所有的"?"都會轉(zhuǎn)換成同一字節(jié),也就失去了他本身的意義.
如果亂碼中不包含"?",那么還是有希望轉(zhuǎn)換回去的,我以上述亂碼中的 "褰╄櫣" 為例重新進(jìn)行了一次轉(zhuǎn)換,代碼如下:
? ? ? ?String str="褰╄櫣"; ? ? ? ? String [] charset=new String[] {"gbk","unicode","utf8","gb2312"}; ? ? ? ? for (int i=0;i<charset.length;i++){ ? ? ? ? ? ? for (int j=0;j<charset.length;j++){ ? ? ? ? ? ? ? ? System.out.println("二進(jìn)制格式: ? "+charset[i]+"編碼格式: ?"+charset[j]); ? ? ? ? ? ? ? ? System.out.println("編碼后的字符串: ?"+new String(str.getBytes(charset[i]),charset[j])); ? ? ? ? ? ? ? } ? ? ? ? }
二進(jìn)制格式: gbk編碼格式: gbk
編碼后的字符串: 褰╄櫣
二進(jìn)制格式: gbk編碼格式: unicode
編碼后的字符串: ?馹
二進(jìn)制格式: gbk編碼格式: utf8
編碼后的字符串: 彩虹
二進(jìn)制格式: gbk編碼格式: gb2312
編碼后的字符串: 褰╄??
二進(jìn)制格式: unicode編碼格式: gbk
編碼后的字符串: ??0%Dj?
二進(jìn)制格式: unicode編碼格式: unicode
編碼后的字符串: 褰╄櫣
二進(jìn)制格式: unicode編碼格式: utf8
編碼后的字符串: ???0%Dj?
二進(jìn)制格式: unicode編碼格式: gb2312
編碼后的字符串: ???0%Dj?
二進(jìn)制格式: utf8編碼格式: gbk
編碼后的字符串: 瑜扳晞婭?
二進(jìn)制格式: utf8編碼格式: unicode
編碼后的字符串: ??閄?
二進(jìn)制格式: utf8編碼格式: utf8
編碼后的字符串: 褰╄櫣
二進(jìn)制格式: utf8編碼格式: gb2312
編碼后的字符串: 瑜扳??婭?
二進(jìn)制格式: gb2312編碼格式: gbk
編碼后的字符串: 褰╄?
二進(jìn)制格式: gb2312編碼格式: unicode
編碼后的字符串: ??
二進(jìn)制格式: gb2312編碼格式: utf8
編碼后的字符串: 彩??
二進(jìn)制格式: gb2312編碼格式: gb2312
編碼后的字符串: 褰╄?
可以看到 其中一種轉(zhuǎn)換方式成功的將亂碼轉(zhuǎn)變回了正常的中文漢字
二進(jìn)制格式: gbk編碼格式: utf8
編碼后的字符串: 彩虹
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
quartz的簡單使用、SpringBoot使用和自定義數(shù)據(jù)源集成方式
這篇文章主要介紹了quartz的簡單使用、SpringBoot使用和自定義數(shù)據(jù)源集成方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教<BR>2024-01-01Spring Boot利用JSR303實現(xiàn)參數(shù)驗證的方法實例
這篇文章主要給大家介紹了關(guān)于Spring Boot利用JSR303實現(xiàn)參數(shù)驗證的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Spring Boot具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05Java數(shù)據(jù)結(jié)構(gòu)之二叉排序樹的實現(xiàn)
二叉排序樹(Binary Sort Tree),又稱二叉查找樹(Binary Search Tree),亦稱二叉搜索樹。本文詳細(xì)介紹了二叉排序樹的原理,并且提供了Java代碼的完全實現(xiàn)。需要的可以參考一下2022-01-01