文章标题

来源:互联网 发布:15单片机和51的区别 编辑:程序博客网 时间:2024/05/10 17:25

0.为什么需要编码,解码,
无论是图片,文档,声音,在网络IO,磁盘io中都是以字节流的方式存在及传递的,但是我们拿到字节流怎么解析呢?这句话就涉及了编码,解码两个过程,从字符数据转化为字节数据就是编码,从字节数据转化为字符数据是解码,可能有人疑问,一个字符不是一个字节,两个字节吗?一堆字符不就是一堆字节吗,需要转什么?好,刚才所说 的以及涉及到编码了,有的编码是一个字节一个字符,就像ASCII码,但是汉字以及其他语言文字太多,很明显一个字节不能表示所有字符,所以才会引申出如 此多的编码,现在主要讨论ISO-8859-1,utf-8,gbk ,其中iso-8859编码是无法对中文进行编码的

    String ISO = "ISO-8859-1";    String UTF = "UTF-8";    String GBK = "GBK";    String string = "很开心分享经验";    byte[] bytes = string.getBytes(ISO);    System.out.println("结果:"+new String(bytes,ISO));    for(byte b:bytes){        System.out.print(b+" ");    }

ISO只要遇到不认识的字符,都会将其用63表示,显示出来,也就是 ? 所有的都变成了问号 这里可以看到六个中文对应6个 ?
顺便说一下,英文不会涉及iso,utf-8,gbk,编码问题,因为英文可以用ASCII码表示,这几种编码对ASCII码兼容
String string = “很开心分享经验 happy the world”;

32代表 空格
utf-8编解码
String ISO = “ISO-8859-1”;
String UTF = “UTF-8”;
String GBK = “GBK”;
String string = “很开心分享经验”;
byte[] bytes = string.getBytes(UTF);

    System.out.println("结果:"+new String(bytes,UTF));    for(byte b:bytes){        System.out.print(b+" ");    }

可以看到 编码没有问题,而且每个汉字占用三个字节 ,一共21个(UTF-16一共占用16个)
GBK编码
代码省略了

编解码还是没有问题, 但是每个汉字占用 两个字节,一共14个
我们知道了,只要遇到一大堆??????这样的乱码,一般可以确认,有一个地方用到了iso的编码,这是中文不能使用的编码除此之外,我们还会看到很多种其他的乱码例如:
1. 寰堝紑蹇冨垎浜粡楠�
2. 2. �ܿ��ķ��?��
3. ºÜ¿ªÐÄ·ÖÏí¾Ñé
4. 很开心分享经éª
这四种乱码,好像都不一样哦,各有各的风采,然而并看不懂。
1. 寰堝紑蹇冨垎浜粡楠� UTF-GBK
2. �ܿ��ķ��?�� GBK-UTF
3. ºÜ¿ªÐÄ·ÖÏí¾Ñé GBK-ISO
4. å¾ˆå¼€å¿ƒåˆ†äº«ç»éª UTF-ISO
(5 裥벀菥袆ꯧ뮏� utf-8 —utf-16

这四种分别对应,不同的编码-解码 例如第一个 很开心分享经验 用utf-8编码后,gbk解码后 就变成了这坨 寰堝紑蹇冨垎浜粡楠� ,剩下三坨不多说
现在总结下(一次编解码):
1. 只要是iso对中文字符编码就一定是一大堆?????,为什么呢,因为我们说了iso把不认识的都转成63 63是什么在ASCII码中是? 而这些编码基本都兼容ASCII,所以
只要是一大坨???????就一定有一个地方采用的是iso编码,而后面还会说道iso编码是很多地方的默认编码(默认的为什么不是utf-8,,郁闷!!!)
2 当我们看到乱码之后,不要慌,第一步先不要想在哪里出现编码问题,先考虑 可能是哪一种编解码 错误。而这种错误是有章可循的。上边内四个基本差不多,(如果还有发现别的,我会再加上)

那么如果出现其他好多次编解码呢? 那这个问题就复杂的多了
1 .String ISO = “ISO-8859-1”;
String UTF = “UTF-8”;
String GBK = “GBK”;
String string = “很开心分享经验”;
byte[] bytes = string.getBytes(UTF);
String string2 = new String(bytes,ISO);
byte[] bytes2 = string2.getBytes(ISO);
String string3 = new String(bytes2,UTF);

    System.out.println("结果:"+string3);

UTF编码,iso解码,iOS解码,utf-8解码 中间经历了曲折,但是,最终由变成了 中文,好艰难
但是我们应该为此庆幸吗? 我觉得不能,你最好也这么觉得,所有的编解码,要统一
统一之前,我们也应该明白,我们总会有疏漏的地方,万一一不留神呢,,所以再看看其他的混合编解码
2.String string = “很开心分享经验”;
byte[] bytes = string.getBytes(UTF);
String string2 = new String(bytes,ISO);
byte[] bytes2 = string2.getBytes(UTF);
String string3 = new String(bytes2,ISO);

    System.out.println("结果:"+string3);

这一次我们 进行了两次 utf编码,ios解码, 但是结果什么样子呢
翻翻前面的,基本差不多,只不过小写变大写,最重要的是长度增加了一倍
3.
byte[] bytes = string.getBytes(UTF);
String string2 = new String(bytes,GBK);
byte[] bytes2 = string2.getBytes(UTF);
String string3 = new String(bytes2,GBK);

    System.out.println("结果:"+bytes2.length+string3);

这一次我们进行了 一个utf-8,编码,gbk解码,utf-8编码,gbk解码,依然是乱码,长度又增加了 二分之一(utf-8表示一个中文三个字节,gbk是两个字节)
4.byte[] bytes = string.getBytes(UTF);
String string2 = new String(bytes,GBK);
byte[] bytes2 = string2.getBytes(GBK);
String string3 = new String(bytes2,UTF);

    System.out.println("结果:"+string3);

这次是utf-8编码,gbk解码,gbk编码,utf-8解码。最终的结果好玩

一部分中文被正确显示,另外一些汉字惨遭抛弃。。。
5.byte[] bytes = string.getBytes(GBK);
String string2 = new String(bytes,UTF);
byte[] bytes2 = string2.getBytes(UTF);
String string3 = new String(bytes2,GBK);

    System.out.println("结果:"+string3);

这一次是GBK编码,utf-8解码,utf-8编码,gbk解码
6、
byte[] bytes = string.getBytes(GBK);
String string2 = new String(bytes,UTF);
byte[] bytes2 = string2.getBytes(GBK);
String string3 = new String(bytes2,GBK);

    System.out.println("结果:"+string3);

这次我们gbk编码,utf-8解码,gbk编码,gbk解码,
得到的结果感人::

一大坨 ??? 刚才我说的都是一大坨 ???编码肯定是iso,但是得有前提,长度相同
到这里,不能在总结了,意思很明确,经过一次两次编码,结果可能已经面目全非,但是仔细分析,每一种情况的具体结果还是不同的,对于我们很多人来说,要想记住这些所有的乱码情况,是不可能的,但是如果我们真的遇到了一些多次编解码,多种编码方式,出现的乱码时,有过这方面的试验经验,或许可以解决的更快一些。
下面从请求处理的流程,以及具体流程的某个阶段可能出现的乱码问题
1.http请求的编码
提交表单(一般设置为method = POST ,一下说的表单默认是post),或者在地址栏直接输入url地址(get请求)
首先先说get方式,也就是输入地址栏 的url
1.http://localhost:8080/TestCharSet/test?charset=中文
红色部分为pathinfo,也就是路径部分,绿色部分是QueryInfo部分,也就是查询字符串,在后台我们一般这样获取
String charset = request.getParameter(“charset”);
以上我在地址栏中输入的,当我从 地址栏复制到word中时,他转成了这个
http://localhost:8080/TestCharSet/test?charset=%E4%B8%AD%E6%96%87
后面的%加上数字字母都是URL编码 加上%是因为16进制表示,所以在前面加个%
URL编码的过程很简单,如下:
1. 将待编码字符原先的存储编码看成一个16进制流【将原2进制流按 字节拆分,每个字节都用2位16进制数表示】;
2. 在每两位16进制数(即一个完整的字节)前加一个%,得到最终编码结果;
对于汉字来说,首先要看其本身存储时所使用的编码是UTF-8还是GB2312。同样的汉字,存储编码不同,经URL编码后的结果自然也不同。例如“川”,使用UTF-8编码存储时为 e5b79d ,经URL编码后则为 %e5%b7%9d ;使用GB2312编码存储时为 b4a8 ,经URL编码后则为 %b4%a8 。
解码的时候也很简单,将编码里的%号去掉,得到一个16进制流,这个16进制流转回2进制流,得到的就是原字符的存储编码。剩下的一个重要问题是怎么理解这个还原出来的存储编码(即原字符使用的存储编码方式)?分三种情况:
· 对于HTTP请求正文中的URL编码,我们可以查看请求头部中 Charset 头域的值,它指定了请求报文所使用的字符集(即存储编码方式)。如图1,因为 Charset 的值为UTF-8,所以我们对解码后的结果就应当按UTF-8编码理解了;
· 因为使用UTF-8编码时,一个汉字的本身存储占三个字节;而使用GB2312编码,一个汉字的本身存储占两个字节。因此如果我们能确定被编码的是纯汉字流的话,我们可以根据解码后的结果占用的字节数是3或者2的倍数来大致推断其存储编码方式;
· 上述方法都不行的话,就只能在译码的时候都试一下了
具体的url编码请查看链接 http://www.tuicool.com/articles/3mUNFz 这里写的很好

前面说到了 浏览器把一个具体编码的字符序列按照url编码 为16进制,同样服务器端,按照url解码,将url进行解码 得到了具体的字符序列,那么服务器拿到了这个字符序列怎么办呢,就可以读取了吗,不能,我们还需要知道这个字符序列是什么编码方式,否则我们根本正常读取不了(否则就会乱码)

2)post方式的编码
例如表单POST请求,会将提交的参数放到请求主体部分

例如name就被放在主体部分,同样,它也是浏览器通过url编码(16进制编码)把数据传送到服务器端,具体的服务器拿到这个16进制解码的结果,也就是这个字符序列,如何处理,是按照什么方式解析呢,同样是 制定的charset值,但是一般表单提交后,服务器端需要我们指定具体的charset进行解码,具体的方式
大家都用过:request.setCharacterEncoding(charset);

3、应用服务器如何解析参数
我们知道url也就是get请求的路径部分,需要解码,解码的方式是charset指定的方式,但是具体的,可能头文件中并没有指定charset,这时 应用服务器会采用默认的编码方式
具体到tomcat中

0 0