JavaWeb的编码问题

几种常见的编码格式

@(深入分析java web技术内幕)

计算机存储单元为1byte = 8 bit,可依表示的字符范围0-255个 2^8,人类的语言符号过多,无法用1byte表示。为了解决问题,引入了一个新的char,一个char由多个byte组成,这样就可以表示更多的字符了,而从char到byte必须编码。

编码可以看作是字典,是人类符号与计算机字节相互转化的字典。各类语言都需要自己的编码,常见的编码有下面几种:

1. ASCII

共有128个,一个字节低7位表示。0~31表示控制字符,32~126是打印字符。

2. ISO-8859-1

ASCII的128个字符不够用,ISO组织在ASCII的基础上定制了一系列标准,拓展ASCII。分别为ISO8859-1 ~ ISO8859-15,其中ISO8859-1覆盖了大部分的西欧字符,最为常用。ISO-8859-1仍然是单字节编码,一共可以表示0~255个字符。

3. GB2312

《信息交换用汉子编码字符集基本集》,双字节编码,A1~A9 用于表示符号,共682个符号,B0~F7表示汉字,包含6763个汉字。

4. GBK

《汉字内码扩展规范》,国家技术监督局为windows95定制的新的汉子内码规范,拓展了GB2312,兼容GB2312,编码范围在8140~FEFE(去掉XX7F)之间,总共有23940个码位,可以表示21003个汉字。双字节编码。

5. GB18030

《信息交换用汉子编码字符集》,它可能是单字节,双字节,四字节编码,与GB2312兼容,虽然 是国家强制标准,但是使用并不广泛。

6.0 Unicode

ISO试图创建一个涵盖所有语言的字典,所以制定了Unicode(Universal Code 统一码 )规范。Unicode是Java和XML的基础。

6.1 UTF-16

UTF-16具体定义了Unicode字符在计算机中的存取方式。UTF-16双字节表示Unicode转化格式,是一种定长表示法,无论什么字符都是用2byte表示,即16bit。UTF-16非常方便,这也是Java以UTF-16作为字符存储格式的重要原因。

6.2 UTF-8

UTF-16虽然使用两个字节表示字符很方便,但是很大一部分的字符无须两个字节来表示,造成了空间的浪费,增大了传输的流量。所以UTF-8针对UTF-16做出了优化,使用变长表示法,,每个编码区域有不同的字码长度,不同类型的字符可以由1-6个字节组成。
规则:

  • 如果一个字节最高位(第八位)为0,表示这是一个ASCII字符(00-7F)
  • 如果一个字节使用 11 开头,连续的11 暗示该字符的字节数,代表UTF-8字符的首字节
  • 如果一个字节使用 10 开头,表示它不是首字节,需要向前查找才能得到当前字符的首字节

Java中需要编码的场景

  1. IO操作
    发生编码问题,通常是字符和字节之间的转化问题引起的。在IO流中,提供了OutputStreamWriter和InputStreamReader桥梁类,供字符和字节转化。创建这两个对象都可以指定一个charset参数,从而指定字符编码,解决乱码问题(仍然是使用StreamEncoder)。
  2. 内存操作
    Java String表示字符串,所以String就提供了转化为字节的方法,比如:s.getBytes("UTF-8")。也可以使用charset类:Charset charset = new Charset("utf-8"); ByteBuffer[] bf = charset.encode(str); CharBuffer cf = charse.decode(byteBuffer);

Java 如何编码解码

Java编码类图

file
// 找到指定的Charset类(Charset有很多子类Unicode、UTF_8、ISO_8859_1)
Charset charset = new Charset("utf-8"); 
ByteBuffer[] bf = charset.encode(str); 
CharBuffer cf = charse.decode(byteBuffer);

Java Web 编码

有IO的地方就会有编码,大部分IO乱码都是网络IO引起的。所有经过网络传输的数据都必须为字节,所以Java序列化必须要实现Serializable接口。

用户发送一个HTTP请求,在URL、Cookies、Parameter上都需要编码。
HTTP请求编码示例:

file

URL 编解码

示例URL:

http://localhost:8080/examples/servlet/add/小猪?author=大侠

http scheme 协议
localhost domain 域名
8080 port 端口号
examples context path,可以在<Contextpath="/examples/"/>中配置
/servlet/add/* servlet path servlet访问路径
小猪 path info

path info 和 query string出现了中文
经过URLDecoder后得到如下链接:

http://localhost:8080/examples/servlet/add/%E5%B0%8F%E7%8C%AA?author=%E5%A4%A7%E4%BE%A0

查阅编码表发现
E5B08F代表UTF-8 小字
E78CAA代表UTF-8猪字
E5A4A7代表UTF-8大字
E4BEA0代表UTF-8侠字
浏览器根据URL编码规范对URL进行编码,按照某种编码格式(一般为UTF-8)将非ASCII字符编码为16进制数字。然后将每个16进制数字前加上"%",从而完成URL编码。

注意,不同浏览器对URL的编码可能不同
在查阅大部分资料发现,firefox以前的版本中,对url进行编码时,path info 会按照utf-8方式进行编码,而query string 则会按照gbk进行编码。

如果不同浏览器对URL的编码不同,那么后台是如何解码的呢?
以Tomcat为例,在server.xml中配置如下即可解决:

<Connector port="8080" protocol="HTTP/1.1"  
           connectionTimeout="20000"  
           redirectPort="8443"  
           URIEncoding="UTF-8"/>

HTTP Header 编码

除了URL上的编码问题,在http header 上页存在编码问题。比如,重定向地址、cookies中都可能存在中文字符。
tomcat在对header解码是在request.getHeader时进行的。具体不再分析

POST 表单提交编码解码

POST表单数据放在http请求体中,当我们在浏览器点击提交按钮时,会根据content-type的charset编码对请求体进行编码。在服务端可以通过contentType进行解码,使用request.setCharacterEncoding(charset)进行解码,然后使用requestParamter()获取参数值,注意编码设置一定在获取之前。

HTTP BODY 编解码

用户请求的数据已经准备完成,服务器会通过response body返回给客户端,可以使用response.setCharacterEncoding(cahrset)对返回的结果进行编码,并且将响应头contentType设置为对应的charset返回给客户端,客户端根据contentType进行解码。如果contentType中没有charset,浏览器则会按照http meta头进行解码,如果没有meta头,浏览器则会按照默认编码进行解码。

JS 编码问题

引入外部js文件

如果引入的js文件中包含字符串输出内容document.write("小红和小明", 如果没有设置charset,就会按照默认的字符编码解析js文件,这样有可能出现乱码问题,我们可以在script标签指定chasert属性:

<script src="xxxx" charset="gbk"/>

如果js文件的编码和引用页面编码一致,则可以不用添加chaset属性。

JS URL 编码

js处理URL编码问题,提供了三个有效的函数:

  1. escape()
    该函数将传入的要解码的字符串中非ASCII的符号和字符全部转换为Unicode编码值,并在前面加上"%u":document.write(escape("哈哈"));得到的结果为%u54C8%u54C8。从而避免了乱码的问题
  2. encodeURI()
    encodeURI()是专门针对URL编码的函数,它会将整个URL的字符(除特殊字符、字母)进行UTF-8编码,并在每个码值前加上"%":encodeURI("http://www.baidu.com/test?a=哈哈")编码为http://www.baidu.com/test?a=%E5%93%88%E5%93%88
  3. encodeURIComponent
    和上面的函数类似,但是该函数会将特殊符号也进行编码。encodeURIComponent("http://www.baidu.com/test?a=哈哈")编码为http%3A%2F%2Fwww.baidu.com%2Ftest%3Fa%3D%E5%93%88%E5%93%88

其他需要编码的地方

XML encoding、 jsp page指定

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值