编码与乱码(05)---GBK与UTF-8之间的转换--转载
原文地址:http://www.blogjava.net/pengpenglin/archive/2010/02/22/313669.html
【GBK轉UTF-8】
在很多論壇、網上經常有網友問“?為什么我使用 new String(tmp.getBytes("ISO-8859-1"), "UTF-8") 或者 new String(tmp.getBytes("ISO-8859-1"), "GBK")可以得到正確的中文,但是使用 new String(tmp.getBytes("GBK"), "UTF-8") 卻不能將GBK轉換成UTF-8呢?”
參考前面的【Java基礎專題】編碼與亂碼(03)----String的toCharArray()方法測試?一文,我們就知道原因了。因為如果客戶端使用GBK、UTF-8編碼,編碼后的字節經過ISO-8859-1傳輸,再用原來相同的編碼方式進行解碼,這個過程是“無損的轉換”---- 因為原始和最終的編碼方式相同。
但是如果客戶端使用GBK編碼,到了服務器端要轉換成UTF-8,或者相反的過程。想一想,字節還是那些字節,但是編碼的規則變了。原來GBK編碼后的4個字節要用UTF-8的每個字符3個字節的規則編碼,怎么能不亂碼呢?
所以從現在開始,不要再犯這種錯誤了。new String(tmp.getBytes("GBK"), "UTF-8") 這個過程,JVM內部是不會幫你自動對字節進行擴展以適應UTF-8的編碼的。正確的方法應該是根據UTF-8的編碼規則進行字節的擴充,即手動從2個字節變成3個字節,然后再轉換成十六進制的UTF-8編碼。
在這個專題的第一篇文章【Java基礎專題】編碼與亂碼(01)---編碼基礎?開頭,我們就已經介紹了這個規則:
?①得到每個字符的2進制GBK編碼
?②將該16進制的GBK編碼轉換成2進制的字符串(2個字節)
?③分別在字符串的首位插入110,在第9位插入10,在第17位插入10三個字符串,得到3個字節
?④將這3個字節分別轉換成16進制編碼,得到最終的UTF-8編碼。
下面給出一個從網絡上得到的Java轉碼方法,原文鏈接見:http://jspengxue.javaeye.com/blog/40781。下面的代碼做了小小的修改
?
package?example.encoding;/**
?*?The?Class?CharacterEncodeConverter.
?*/
public?class?CharacterEncodeConverter?{
????/**
?????*?The?main?method.
?????*?
?????*?@param?args?the?arguments
?????*/
????public?static?void?main(String[]?args)?{
????????try?{
????????????CharacterEncodeConverter?convert?=?new?CharacterEncodeConverter();
????????????byte[]?fullByte?=?convert.gbk2utf8("中文");
????????????String?fullStr?=?new?String(fullByte,?"UTF-8");
????????????System.out.println("string?from?GBK?to?UTF-8?byte:??"?+?fullStr);
????????}?catch?(Exception?e)?{
????????????e.printStackTrace();
????????}
????}
????/**
?????*?Gbk2utf8.
?????*?
?????*?@param?chenese?the?chenese
?????*?
?????*?@return?the?byte[]
?????*/
????public?byte[]?gbk2utf8(String?chenese)?{
????????
????????//?Step?1:?得到GBK編碼下的字符數組,一個中文字符對應這里的一個c[i]
????????char?c[]?=?chenese.toCharArray();
????????
????????//?Step?2:?UTF-8使用3個字節存放一個中文字符,所以長度必須為字符的3倍
????????byte[]?fullByte?=?new?byte[3?*?c.length];
????????
????????//?Step?3:?循環將字符的GBK編碼轉換成UTF-8編碼
????????for?(int?i?=?0;?i?<?c.length;?i++)?{
????????????
????????????//?Step?3-1:將字符的ASCII編碼轉換成2進制值
????????????int?m?=?(int)?c[i];
????????????String?word?=?Integer.toBinaryString(m);
????????????System.out.println(word);
????????????//?Step?3-2:將2進制值補足16位(2個字節的長度)?
????????????StringBuffer?sb?=?new?StringBuffer();
????????????int?len?=?16?-?word.length();
????????????for?(int?j?=?0;?j?<?len;?j++)?{
????????????????sb.append("0");
????????????}
????????????//?Step?3-3:得到該字符最終的2進制GBK編碼
????????????//?形似:1000?0010?0111?1010
????????????sb.append(word);
????????????
????????????//?Step?3-4:最關鍵的步驟,根據UTF-8的漢字編碼規則,首字節
????????????//?以1110開頭,次字節以10開頭,第3字節以10開頭。在原始的2進制
????????????//?字符串中插入標志位。最終的長度從16--->16+3+2+2=24。
????????????sb.insert(0,?"1110");
????????????sb.insert(8,?"10");
????????????sb.insert(16,?"10");
????????????System.out.println(sb.toString());
????????????//?Step?3-5:將新的字符串進行分段截取,截為3個字節
????????????String?s1?=?sb.substring(0,?8);
????????????String?s2?=?sb.substring(8,?16);
????????????String?s3?=?sb.substring(16);
????????????//?Step?3-6:最后的步驟,把代表3個字節的字符串按2進制的方式
????????????//?進行轉換,變成2進制的整數,再轉換成16進制值
????????????byte?b0?=?Integer.valueOf(s1,?2).byteValue();
????????????byte?b1?=?Integer.valueOf(s2,?2).byteValue();
????????????byte?b2?=?Integer.valueOf(s3,?2).byteValue();
????????????
????????????//?Step?3-7:把轉換后的3個字節按順序存放到字節數組的對應位置
????????????byte[]?bf?=?new?byte[3];
????????????bf[0]?=?b0;
????????????bf[1]?=?b1;
????????????bf[2]?=?b2;
????????????
????????????fullByte[i?*?3]?=?bf[0];????????????
????????????fullByte[i?*?3?+?1]?=?bf[1];????????????
????????????fullByte[i?*?3?+?2]?=?bf[2];
????????????
????????????//?Step?3-8:返回繼續解析下一個中文字符
????????}
????????return?fullByte;
????}
}
最終的測試結果是正確的:string from GBK to UTF-8 byte:? 中文。
但是這個方法并不是完美的!要知道這個規則只對中文起作用,如果傳入的字符串中包含有單字節字符,如a+3中文,那么解析的結果就變成:string from GBK to UTF-8 byte:? ?????????中文了。為什么呢?道理很簡單,這個方法對原本在UTF-8中應該用單字節表示的數字、英文字符、符號都變成3個字節了,所以這里有9個?,代表被轉換后的a、+、3字符。
所以要讓這個方法更加完美,最好的方法就是加入對字符Unicode區間的判斷
| UCS-2編碼(16進制) | UTF-8 字節流(二進制) |
| 0000 - 007F | 0xxxxxxx |
| 0080 - 07FF | 110xxxxx 10xxxxxx |
| 0800 - FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
漢字的Unicode編碼范圍為\u4E00-\u9FA5 \uF900-\uFA2D,如果不在這個范圍內就不是漢字了。
【UTF-8轉GBK】
道理和上面的相同,只是一個逆轉的過程,不多說了
但是最終的建議還是:能夠統一編碼就統一編碼吧!要知道編碼的轉換是相當的耗時的工作
轉載于:https://www.cnblogs.com/davidwang456/p/4841202.html
總結
以上是生活随笔為你收集整理的编码与乱码(05)---GBK与UTF-8之间的转换--转载的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 接口设计备忘录
- 下一篇: java解压多目录Zip文件(解决中文乱