字符编码问题之手动转码并不万能

来源:互联网 发布:中国税务网络大学app 编辑:程序博客网 时间:2024/06/06 14:16
  • 引言
相信大家在最开始接触web的时候,因为Tomcat服务器默认的URIEncoding是ISO8859-1。
应该产生过中文乱码问题,某些前辈就会告诉你,可以手动转码。

new String(str.getBytes("IOS8859-1"),"gbk");

这句话大家应该不陌生,如果页面编码方式是gbk,Tomcat URIEncoding是ISO8859-1。
这样编码再解码,看着确实合理,而且能解决问题。
但是其实手动转码局限性是比较大的。


  • 基本概念介绍
首先你应该知道编码是干什么,编码就是把字符按照规定的字符集转换成 0 1 的字节流形式.
解码,是把相应的0 1字节流按照规定字符集转换成相应字符的形式。

然后你应该知道,Java中的字符都是按照Unicode字符集进行编码的。 
也就是说,JVM管理的内存中的,字符都是以Unicode编码形式存在的。
而非JVM的内存中,字符是以各种各样的字符集进行编码的,当Java程序与外界交互的时候(如I/O),就会涉及到编码解码问题。
在Java中,不同的字符集都是以Unicode作为桥梁相互转换的。

  • String两个常用API原理介绍。

public static void main(String[] args) throws Exception {String str = "天";byte[] b = str.getBytes("gbk");show(b);}    public static void show(byte[] b){    for (int i = 0; i < b.length; i++){    System.out.print( b[i] + " ");    }    System.out.println();    }

结果:-52 -20

byte[] b = str.getBytes("gbk");

这句话的意思是把str中的字符 天 用gbk字符集进行编码,返回编码后的字节数组。

把-51 -20的2进制的表示形式就是 天 在gbk字符集中的编码。

看着简单,实际上是进行了以下的操作。

相当于从内存中取出天字的Unicode编码,然后利用Unicode到gbk的转换算法,得到天字的gbk编码

如图1




String str2 = new String(b,"gbk");

这句话的意思是把字节数组b,用gbk字符集解码生成新的字符串。

实际上是把字节数组b通过gbk到unicode的转换算法转换成unicode码

如图2



  • 手动转码
public class Test4 {public static void main(String[] args) throws Exception {String str = "天";byte[] b = str.getBytes("gbk");show(b);String str2 = new String(b,"utf-8");//类似I/O时,用了与发送方不匹配字符集。导致下面需要手动转码byte[] b2 = str2.getBytes("utf-8");//企图转回来show(b2);System.out.println(new String(str2.getBytes("utf-8"),"gbk"));}    public static void show(byte[] b){    for (int i = 0; i < b.length; i++){    System.out.print(b[i]+ " ");    }    System.out.println();    }}


结果如下:
-52 -20 
-17 -65 -67 -17 -65 -67 
锟斤拷


上面的过程与我们开头提到的web中手动转码的问题非常相像。
但是,这次为什么失败了呢。先让我们看看上面代码做了什么。

相当于
1.首先把str通过gbk编码得到了字节数组b
2.把字节数组b用uft8字符集解码生成新的字符str2
3.把str2字符用utf-8字符集编码成字节数组b2

(2)(3)看似可逆。

实际上是

1.首先把str通过gbk编码得到了字节数组b
2.利用字节数组b,通过utf-8到unicode的转换算法生成新的字符str2
3.把字符串str2利用utf-8到unicode的转换算法解码生成字节数组b2

为什么b的值与b2的值不同呢?
现是字节数组通过utf-8解码生成字符,然后字符通过utf-8编码生成字节数组。
明明是可逆的操作,为什么不能还原.让b2与b相等呢?

确实这种转换算法实际上是可逆的。
但问题出现在哪了呢?
其实大家忽略了一个细节我们的b字节数组里面的值是gbk编码形式的。
把一个gbk形式的字节流 通过utf-8到unicode转换算法 转成 unicode码。这个过程之中因为utf-8与gbk的不兼容。导致了转换过程出现了错误
所以在,你想转回来的时候,你用错误的结果无法转换回来


再来看看最开头提到的例子:


public class Test4 {public static void main(String[] args) throws Exception {String str = "天";byte[] b = str.getBytes("gbk");show(b);String str2 = new String(b,"ISO8859-1");byte[] b2 = str2.getBytes("ISO8859-1");show(b2);System.out.println(new String(str2.getBytes("ISO8859-1"),"gbk"));}    public static void show(byte[] b){    for (int i = 0; i < b.length; i++){    System.out.print(b[i]+ " ");    }    System.out.println();    }}



结果如下:

-52 -20 
-52 -20 


为什么用ISO8859-1就成功了呢?为什么用ISO8859-1就可逆了呢?

如果详细说会比较麻烦,你可以认为在这种操作的时候ISO8859-1和gbk的兼容性比较好。

如果你觉得只说兼容性这个词太抽象的话,可以稍微这么理解。

b[]字节数组里面存的是gbk编码,如果用utf-8到unicode的转换算法。这个字节数组里面的某些位用这种算法无法解析,于是数据就出现了错误,数据错误之后,你想转换回来当然不行了(所谓的兼容性不好)。

b[]字节数组里面存的是gbk编码,如果用iso8859-1到unicode的转换算法。虽然字节数组的某些位不是标准的iso8859-1的格式,但是这种算法仍然能按照某种方式解析,所以数据没出现错误,所以你想转换回来的时候,还能转换回来(所谓的兼容性好)。





所以说,手工转码不是万能的。


最佳实践:事先调整好发送方与接收方的字符集,最好别手工转码。




以上纯属个人观点,欢迎讨论。










原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 捡到苹果8p手机怎么办才能自己用 用力按压导致玻尿酸变形移位怎么办 华为麦芒5应用锁密码忘了怎么办 华为麦芒6应用锁密码忘了怎么办 华为手机的设置不在桌面了怎么办 华为手机所有应用都不在桌面怎么办 华为麦芒5设置页面不显示怎么办 华为麦芒5主屏页面不显示怎么办 6s p换屏幕原装太贵怎么办 4g手机开不开机黑屏怎么办 华为麦芒5 4g信号差怎么办 华为麦芒手机锁屏密码忘了怎么办 华为麦芒5相机拍相片倒了怎么办 红米5a开不了机怎么办 华为沾了海水打不开机怎么办 华为麦芒手机忘记锁屏密码怎么办 华为手机的方框键摁不了怎么办 笔记本自动更新到一半太慢了怎么办 华为麦芒5音量下键乱跑了怎么办 麦芒6手机QQ视频没声音怎么办 18:9看16:9黑边怎么办 华为畅享7s声音小怎么办 华为畅享8手机声音小怎么办 华为畅享8plus声音小怎么办 荣耀7x锁屏密码忘记怎么办 华为荣耀7x锁屏密码忘记了怎么办 苹果耳机进水后声音变了怎么办 华为荣耀开了数据用不了怎么办 华为手机高德地图信号弱怎么办? 手机QQ浏览器看视频有广告怎么办 手机显示网络连接但不可上网怎么办 华为手机关机后开不了机怎么办 华为畅享8p相机拍照模糊怎么办 手机触屏不准怎么办荣耀青春版九 华为手机锁屏手势密码忘了怎么办 荣耀手机锁屏密码忘了怎么办 华为p20隐私空间密码忘了怎么办 安全管家隐私保护的密码忘了怎么办 华为手机自带截图键删除了怎么办 飞科电吹风吹一会就断电怎么办 住酒店时电吹风吹坏了怎么办