字符集与字符编码

字符集:是人认别的 字符集合 

 

字符编码:计算机存储是编码

ASCII

ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)。ASCII字符集包含128个字符,它用8bit表示,其中,第一位用0表示。ASCII编码则是一种将ASCII字符集中的字符映射到二进制数的关系。

譬如:A -> 65,B -> 66 ,C->67  其中A B C是字符集 65 66 67 在计算机中的二进制为字符编码

Unicode

为了整合全世界的所有语言文字,我们的先辈们想出了一个方法Unicode(Universal Mutiple-Octet Coded Character Set),它只是字符集,却没有规定这个二进制该如何存储、传输

其中,它的字符集称为Universal Character Set (UCS),它规定了需要多少字节存储字字符,分别有2个字节和4个字节,各自对应UCS-2、UCS-4。而UTF(Unicode Transformation Format)规定了字符如何传输和存储。UTF又分为UTF-8、UTF-16和UTF-32

String s = "这是一段中文编码"; 

byte[] b = s.getBytes("UTF-8");

String UTF-8 = new String(b, "UTF-8");

System.out.println(UTF-8);

byte[] b1 = s.getBytes("GBK");

String GBK = new String(b1, "GBK");

System.out.println(GBK1);

java的String内部使用16位空间存储字符,也就是Unicode字符

UTF-8不会一口气转换成GBK,中间必须使用Unicode字符来过渡

UTF-8 ----->Unicode------>GBK

Java Web中涉及到的编解码问题

深入理解JavaWeb技术内幕之中文编码 - 高适 - 博客园

在Web中常见的就是发起http请求。此时会有三个地方存在编码:URL,Cookie,Parameter.

URL

比如输入下面的一个URL

http://localhost:8080/examples/servlets/servlet/例子?author=编码

就以Tomcat为例:

很显然,这是一个GET请求。在PathInfo和QueryString部分都出现了中文。在Firefox浏览器中,PathInfo 是UTF-8编码, QueryString是GBK编码。
而且,URL还有一条规则就是,会将非ASCII码的字符按照某种编码格式编码成16进制数字之后,前面再加上%号

在Tomcat的安装目录下有一个Conf文件夹,里面有一个配置文件server.xml,打开该文件,如下图所示。

会发现,URI编码是“UTF-8”,如果没有的话,默认为 ISO-8859-1.所以有中文URL时,最好将URIEncoding设置为UTF-8编码。

解析QueryString:

该解码字符集要么是Header中定义的Charset,要么是默认的ISO-8859-1。要使用ContentType中定义的编码,就要设置。

我说的是从根本上解决Request.QueryString中文乱码问题。就是说:Url必须得是:
http://www.abc.com/index.aspx?key=中文
一些“曲线救国”或者迂回的方法,比如说:对先对“中文”编码(escape、Server.UrlEncode)再传值的都不能算从根本上解决此问题,只能算是回避了QueryString中文乱码的问题。因为这样传的参数是“%u4E2D%u6587”或者“%e4%b8%ad%e6%96%87”而不是中文。这个问题是可以解决的,因为直接传中文在我其他的网站里面是不会乱码的,就是说这个情况偶然,或者有些细节的地方我没有注意到。
经测试以下解决方案无效: (注:网站整站是utf-8的,而且也相信utf-8可以兼容中文)
1、Request.ContentEncoding = Encoding.GetEncoding("utf-8");
2、web.config添加配置节点:<globalization fileEncoding="utf-8" requestEncoding="utf-8" responseEncoding="utf-8" culture="zh-CN"/>

所以,为了避免像上面那么麻烦应该尽量避免在URI中使用非ASCII字符。

HTTP Header的编解码

同理,不要在Header中传递非ASCII字符,如果一定要传递,先将这些字符用
org.apache.catalina.util.URLEncoder编码,然后添加到Header中。

POST表单

因为POST表单的参数提交方式与QueryString不同,它是通过HTTP的BODY传递到服务器端的。

页面上单击提交按钮,浏览器首先将根据ContentType的Charset编码格式对在表单中插入的参数进行编码,然后提交到服务器,在服务端,同样也是按照ContentTYpe的字符集进行解码。所以POST表单提交的参数一般不会出问题。

设置字符集编码的方式:

request.setCharacterEncoding(charset);

注意:一定要在第一次调用request.getParameter方法之前就对编码进行设置。否则会按照默认的ISO-8859-1进行解析。

HTTP BODY

当用户请求的资源已经成功获取之后,这些内容将通过Response返回给客户端浏览器。这个过程需要先编码再解码。编解码的字符集设置

response.setCharacterEncoding(charset);

并且通过Header的Content-Type返回给客户端,浏览器接收到返回的Socket流时将通过Content-Type的charset进行解码。

如果没有相应的设置,则浏览器将按照HTML中的中的charset进行解码。

JDBC

如果是MySQL,可以通过URL进行设置。

URL:“jdbc:mysql://localhost:3306/DB?useUnicode=true&characterEncoding=GBK”.

JS

引入js文件的编码格式与当前页面的编码格式不一样,会发生乱码。

JS中处理URL编码的函数有如下三个:

1.escape()

功能:将ASCII字母,标点符号,数字之外的其他字符转换成Unicode编码值,并且在编码值前加上"%u".

2.encodeURI()

功能:将这个URL中的字符进行UTF-8编码,并且在每个码值前加%。

解码用decodeURI()

3.encodeURIComponent()

功能:除了对少数标点符号,字母,数字不编码之外,对其他任何符号都编码,通常用于将一个URL参数放在另外一个URL中。

--------------------------------

http请求的接收,以及解码过程

需不需要设置解码呢?

答案是肯定的,手动设置一下肯定是非常好的。

uri/url的解码过程

下面我们以tomcat为例,当上面那一大串byte数组传输到服务器后,首先,tomcat会对uri部分进行解码,这里charset由tomcat配置文件中的<Connector URIEncoding="UTF-8"决定,不设置的话默认值为ISO-8859-1。由于域名和端口只会是英文,比如说:www.baidu.com,www.google.com;同时我们上面讲过,在uri部分我们不会去用中文,我们只会采用英文。因此,对于url这一块,无论是采用UTF-8还是ISO-8859-1解码,url这部分内容都不会乱码。到这一步,我们服务器端就解码得到了localhost:8082/article/queryByTitle

queryString的解码过程

上面,我们知道了uri/url这一块的内容解码过程。下面,我们来看看queryString这部分的解码过程。

我们在chrome中,F12查看http请求的详细信息,可以发现查询参数被作为parameters保存了下来。这里,可以告诉大家,通过GET方式发出http请求所携带的queryString以及通过POST方式发出http请求所携带的表单参数,也就是GET、POST这2种方式携带的参数,都会被作为parameters保存;在服务器端通过request.getParameter()方法可以获取到值。

clipboard.png

下面以GET方式的queryString的解码过程来讲解。首先我们思考一下,如何才能得到原来的中文呢?当然是先把Hex数字使用UTF-8转码一次,得到浏览器初步处理的参数,如:title=JVM%20%E8%B0%83%E4%BC%98,然后再对原来非ASCII字符的部分使用UTF-8转码一次,得到最初值:title=JVM 调优。中文这一块我也没找到相关资料,我就分析一下英文的解码过程吧。

1.英文参数的情况

假设我们原有的请求并不含中文参数,比如说:title=JVMoptimize 。那么在服务器端调用request.getParameter的方法时,会先进行转码,而charset由http请求的header中的contentType决定,否则使用ISO-8859-1。要想使用contentType的charset,还需要把<Connector URIEncoding="UTF-8" useBodyEncodingForURI="true" />设置为true,注意,true只是设置queryString的解码。到这里,对Hex的解码就会采用contentType的UTF-8进行解码了,我们就可以还原得到title=JVMoptimize了,然后取到JVMoptimize这个参数值。

2.中文参数的情况

我们再来讲一讲中文参数的问题,因为在chrome中,非ASCII字符会采用UTF-8先进行一次编码;那么其实在服务器端,我们可以自己思考一下。因为如果是纯英文的参数,下图是title=JVM optimize的请求,在初步处理时,空格是Hex的%20,也就是说纯英文下%后面一定是20,%20代表了一个空格。而如果是中文参数,因为浏览器手动在每一个字节前加了%,注意,这样我们服务器端通过%和其后面的是否跟着20就可以知道这里是空格还是代表着中文参数了。

clipboard.png

queryString解码的猜想

我们可以做出设想,在对queryString进行第一次转码之后,可能有2种基本情况:

  • 纯英文,还有空格的参数。原参数如:title=JVM optimize 。
    那么第一次转码,会得到:title=JVM%20optimize 。
  • 中文,还有空格的参数。原参数如:title=JVM 调优
    那么第一次转码。会得到:title=JVM%20%E8%B0%83%E4%BC%98

这个时候的结果就很明确了。我猜测,这时候服务器内部应该会对%进行检测,然后做出相应的处理。下面是我的猜测:

如果检测到%,那么看看其后面是否跟着20?是的话,就表示这是一个空格,将其转化(解码)为空格" " ;不是的话,代表这一块将是中文字符(或其他非ASCII字符,如日文,韩文),就把这一块连续的%剔除,再用UTF-8转码,就得到了中文字符。

上面应该是挺靠近真实情况的解释了,因为肯定对非ASCII的解码要进行2次,上面的依据也挺充足的。

其他

那么我已经对url的编码解码过程做了一个分析,要注意的点和需要设置的地方在文中已经提到了。对于网上常见的一个设置:request.setCharacterEncoding(charset),设不设置都无所谓了,它的作用和contentType是一样的。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值