本文首发于微信公众号【程序员江湖】
作者黄小斜,斜杠青年,某985硕士,阿里 Java 研发工程师,于2018 年秋招拿到 BAT 头条、网易、滴滴等 8 个大厂 offer
个人擅长领域 :自学编程、技术校园招聘、软件工程考研(关注公众号后回复”资料“即可领取 3T 免费技术学习资源)
Java作为跨平台语言,涉及到的编解码问题也比较多,我们来探讨一下HTTP请求如何控制编码格式。
几种常见的编码格式
为什么要编码
由于计算机只能看懂01,人类只能看懂字符,所以要进行编码和解码。
1 在计算机中,一个字节是最小的信息存储单元,而一个字节只能表示256个字符。
2 人类需要的符号远远超过256个。
如何翻译
各种语言需要交流,如何进行翻译呢,在计算机中提供了常见的翻译方式。比如ASCII,UTF-8等编码方式,他们可以被看做字典,可以让字节数组变成对应的字符。那么他们有什么区别呢。
1 ASCII码
一共有128个,用一个字节的低七位表示。
2 ISO
128个字符显然不够用,于是在ASCII基础上建立了ISO-8559编码。虽然仍是单字节,但可以表示256字符。
3 GB2312
是双字节编码,可以表示6763个汉字
4 GBK
它扩展了GB2312,功能表示21003个汉字。他和GB2312相互兼容
5 UTF-16
首先说下Unicode,ISO试图简历一个超语言词典,让所有语言都可以通过这个字典翻译。这是非常复杂的。
UTF-16定义了Unicode字符在计算机中的存储方法,UTF-16用两个字节来表示Unicode的转化格式,并且使用定长的方法,无论什么字符都可以用2个字节表示。
这大大简化了字符串操作,所以Java使用UTF-16作为内存字符的存储格式。
6 UTF-8
UTF-16统一使用两个字节表示一个字符,虽然很简单,但是很多字符只需要一个字节就可以表示了,相当于多处用了一倍的空间。
在网络带宽有限的情况下,没有必要。UTF-8使用变长技术,每个编码区有不同的字码长度。所有字符可以用1-6个字节组成。
如果是一个字节的字符那么就是ASCII码了
Java中需要编码的场景
io操作中存在的编码
字节流到字符流的互相转换需要用到编码
内存操作中的编码
使用String字符串和byte数组进行相互转换时,需要指定编码
Java中如何编解码
一般指定charsetname为指定编码格式即可。
各种编码实践
略
对几种编码格式的比较
1 对于中文字符,使用GBK好一点
2 对于Unicode字符,使用UTF-16的编码效率较高,转换更简单,进行字符串操作也更好,适合在磁盘和内存之间使用,但是不适合进行网络传输,因为双字节容易损坏,且占用空间大。
3 UTF-8更适合网络传输,UTF-8使用单字节存储,单个字符损坏不影响其他字符。
Java Web中设计的编解码
对于http网络传输,考虑压缩数据,减少网络传输量,但是有的压缩算法只减少字符数但是不减少字节数。
在计算机中,汉字的表示方式一般是一个字符使用两个字节来表示,所以Java的char类型也是16个bit,也就是2个字节。
JavaWeb可能出现编码转换的地方:
1 HTTP请求中的URI,Cookie,Post表单参数需要解码。
2 数据库的读写,文件读写需要编解码。
3 所有数据从socket返回时需要编码,浏览器解析页面需要解码。
URL的编解码
一个URL可以被分为好多个组成部分
http://localhost:8080/hello/servlet/黄蓬龙?name=h2pl
scheme domain port context servletpath pathinfo querystring
这部分中,url不包括querystring,uri则只包括hello/servlet/黄蓬龙,也就是context+servletpath+pathinfo。
所以uri只是定位资源,但是不包括网络协议
对于Tomcat而言,servlet会把这个url解析为一下几个部分。
事实上一部分配置是在server.xml配置文件中定义的。
1 port是配置文件中的connector port定义的。
2 context path是在配置文件中定义的
3 servletpath是web.xml的url-pattern中定义的。
4 pathinfo是我们具体请求的servlet
5 querystring是要传递的参数
pathinfo采用utf-8编码,querystring采用gbk编码
总结就是,url的编解码过程比较复杂,不是我们在应用中能控制的,所以尽量避免在url中使用非ASCII字符。
HTTP header的编解码
同理,默认使用ISO编码且不能改变,所以不用有非ASCII字符。
POST表单的编解码
post使用body传递数据,而header中设置了contenttype,所以我们根据这个编码格式进行编解码即可。
http body的编解码
上面讲过了。
另外JDBC的编码设置要和数据库内置的数据编码保持一致。
在JS中的编码问题
外部引入JS文件
引入时需要设置编码,否则可能乱码
JS的URL编码
略
其他需要编码的地方
1 xml可以设置头指定编码格式
2 velocity模板设置编码
3 jsp设置编码
常见问题分析
中文字符变乱码
编码解码的字符集不一致
一个汉字变成问号
将中文经过ISO编码(不支持中文)后会变成?
一个汉字变成两个问号
可能是多次编码过程中出错。
不正常的正确编码
略