前言
URL只能使用英文字母、阿拉伯数字和某些标点符号,不能使用其他文字和符号。比如,世界上有英文字母的网址"http://www.abc.com",但是没有希腊字母的网址"http://www.aβγ.com"(读作阿尔法-贝塔-伽玛.com)。这是因为网络标准RFC 1738做了硬性规定:
"...Only alphanumerics [0-9a-zA-Z], the special characters "$-_.+!*‘()," [not including the quotes - ed], and reserved characters used for their reserved purposes may be used unencoded within a URL."
"只有字母和数字[0-9a-zA-Z]、一些特殊符号"$-_.+!*‘(),"[不包括双引号]、以及某些保留字,才可以不经过编码直接用于URL。"
这意味着,如果URL中有汉字,就必须编码后使用。但是麻烦的是,RFC 1738没有规定具体的编码方法,而是交给应用程序(浏览器)自己决定。这导致"URL编码"成为了一个混乱的领域。
如下:
1:地址栏输入和点击url访问浏览器时,chrome下无论请求地址和参数,均经过utf-8编码;非chrome浏览器,其中请求地址用utf-8编码,参数按操作系统编码进行编码(ie下的编码参数部分都不带%)。但参数中含特殊符号时,未确定何特殊符号会被编码。
例子:http://www.google.com/小明|=?param=小明|= a.上面的url在中文xp的chrome下,发送的请求地址是 http://www.google.com/%E5%B0%8F%E6%98%8E%7C=?param=%E5%B0%8F%E6%98%8E|= b.在中文xp的firefox下,发送的请求地址是 http://www.google.com/%E5%B0%8F%E6%98%8E%7C=?param=%D0%A1%C3%F7|= c.在中文xp的ie下,发送的请求地址是 http://www.google.com/%E5%B0%8F%E6%98%8E%7C=?param=С??|=
2:超链接中的请求,请求地址用utf-8编码,参数用页面编码,但是在ie下的参数不带%号。
在上面的例子中,在页面指定utf-8编码下,参数部分会变成:param=%E5%B0%8F%E6%98%8E|=。但ie会变成param=E5B08FE6988E|=。
3:js进行超链接访问时,和超链接情况一致。
4:ajax异步请求时,ie总是用gbk来编码,包括网页路径和参数,非ie和超链接请求一致。
在上面例子中,ie下发送的请求会变成:http://www.google.com/%D0%A1%C3%F7%7C=?param=d0a1c3f7|=
form提交涉及到的编码
form 表单中有一个 accept-charset 属性,如果没有此属性会用页面编码来提交,如果页面编码没有就是 utf-8编码。
两个应用编码不同,一个是GBK编码,另一个是UTF-8编码。现在要在GBK编码的应用里使用表单向UTF-8编码的应用里提交数据,很显然,如果不做特殊处理的话,会出现乱码。 解决方案: 在GBK编码的页面里编写如下代码: <form method="post" action="..." accept-charset="utf-8"> ... </form> 如此的代码在Firefox等正常的浏览器下没有任何问题,但是遇到IE这个变态浏览器就不灵光了,我们还得用点不入流的手段Hack一下: <form method="post" action="..." accept-charset="utf-8" onsubmit="document.charset=‘utf-8‘;"> ... </form>
ajax 中涉及到的编码
ajax 提交到服务器的编码上面已经讨论过。但是获取后台内容也会有乱码的情况出现:
如果ajax 后台返回的不是utf-8的编码的二进制流,那么在xmlhttprequest 对象的responseText 方法中字符就会乱码,因为浏览器默认用utf-8来解码。
我在后台返回时的conent-type 中也明确书写了charset :gbk 。但是实测没起到作用。 这个等有时间在验证
注意:
无论何种方式提交的服务器,浏览器都会转化成ascll字符,然后以ascll对应的码点转成二进制进行传输。对于那么不能直接转成ascll字符的,比如汉字。也会先用以上的编码方式转成ascll字符。 比如: 春节 ==》%E6%98%A5%E8%8A%82 utf-8编码 ==》 %B4%BA%BD%DA gbk 编码 在utf-8编码下,一个汉字 会有9个字节进行传输。 在一个html标签的属性中会有长度的限制。比如 a 标签的href 的长度限制,计算这个长度时要注意以上的细节
由于各个浏览器处理起来的不一致,所以我们需要用js来处理编码,因为js的处理结果都是一致的,都是utf-8编码。三个函数:
1.escape()
虽然这个函数现在已经不提倡使用了,但是由于历史原因,很多地方还在使用它,所以有必要先从它讲起。 实际上,escape()不能直接用于URL编码,它的真正作用是返回一个字符的Unicode编码值。比如"春节"的返回结果是%u6625%u8282,也就是说在Unicode字符集中,"春"是第6625个(十六进制)字符,"节"是第8282个(十六进制)字符。
它的具体规则是,除了ASCII字母、数字、标点符号"@ * _ + - . /"以外,对其他所有字符进行编码。在\u0000到\u00ff之间的符号被转成%xx的形式,其余符号被转成%uxxxx的形式。对应的解码函数是unescape()。
escape对0-255以外的unicode值进行编码时输出%u****格式,其它情况下escape,encodeURI,encodeURIComponent编码结果相同。
所以,"Hello World"的escape()编码就是"Hello%20World"。因为空格的Unicode值是20(十六进制)
首先,无论网页的原始编码是什么,一旦被Javascript编码,就都变为unicode字符。也就是说,Javascipt函数的输入和输出,默认都是Unicode字符。这一点对下面两个函数也适用。
其次,escape()不对"+"编码。但是我们知道,网页在提交表单的时候,如果有空格,则会被转化为+字符。服务器处理数据的时候,会把+号处理成空格。所以,使用的时候要小心。可以这样处理下 str.replace(/+/g,"%u002B");
escape不编码字符有69个:*,+,-,.,/,@,_,0-9,a-z,A-Z
2.encodeURI()
encodeURI()是Javascript中真正用来对URL编码的函数。 它着眼于对整个URL进行编码,因此除了常见的符号以外,对其他一些在网址中有特殊含义的符号"; / ? : @ & = + $ , #",也不进行编码。编码后,它输出符号的utf-8形式,并且在每个字节前加上%。它对应的解码函数是decodeURI()。 需要注意的是,它不对单引号‘编码。
3.encodeURIComponent()
最后一个Javascript编码函数是encodeURIComponent()。与encodeURI()的区别是,它用于对URL的组成部分进行个别编码,而不用于对整个URL进行编码。 因此,"; / ? : @ & = + $ , #",这些在encodeURI()中不被编码的符号,在encodeURIComponent()中统统会被编码。至于具体的编码方法,两者是一样。它对应的解码函数是decodeURIComponent()。encodeURIComponent不编码字符有71个:!, ‘,(,),*,-,.,_,~,0-9,a-z,A-Z
上面说到form post提交的时候可以设置 charset。那ajax提交的时候怎么办?比如怎么把上面三个函数处理过的utf-8编码的字符发送到GBK编码的后台
首先,当我们使用提交时,浏览器会根据当前页面编码,encode一次,然后发送到服务端,服务端接收到表单,会自动dencode一次,通常这个过程是对程序是透明的。即,浏览器把请求翻译成ascll字符传输,服务端接受二进制并翻译成ascll进行dencode。
这个dencode 和前端的encode 的编码不一致问题就来了。
解决:
1.使用escape函数 把中文换成%uxxxx的形式提交。这样浏览器encode的时候不会在处理了。到了后台,dencode完也是这种形式。 之后再用utf-8转一下。
2.
1.在A应用处用encodeURIComponent()2次编码参数数据,如k=中文(utf-8编码为%E4%B8%AD%E6%96%87),进行2次编码后,encodeURIComponent(encodeURIComponent(k))=%25E4%25B8%25AD%25E6%2596%2587 2.将请求发送到url2 = http://www.xxx.com/a.htm?k=%25E4%25B8%25AD%25E6%2596%2587&encoding=utf-8 3.url2对应应用收到这个请求后,web容器会对%25进行解码,变成%,对应request.getParameter("k")=%E4%B8%AD%E6%96%87 4.再URLDecoder.decode("%E4%B8%AD%E6%96%87", "UTF-8")就能解码回原来的中文而不会乱码了。
从编码到页面呈现中涉及的编码
我们在开发过程中新建文件(js,css,html),都会有默认的编码方式存储在硬盘上。不过的开发工具新建文件时的编码方式不同。
源代码发布到web容器后,用户在浏览器访问我们的文件,web容器以二进制流发送我们的文件给浏览器。
浏览器要解析这个二进制流。那么问题来了,浏览器用什么编码去解析?
1.根据http协议中的 content-type 中的charset ,content-type中的charset的优先级最高,如果有charset选项浏览器将忽略以下规则
2.html页面中meta标签中的charset。(js,css 也有charset ,如果有将用此charset ,如果没有就用宿主html的charset)
3.浏览器默认charset
浏览器解析完这个二进制流之后,会把这些信息存在变量中。 变量在内存中存储的编码方式就是浏览器默认的了,跟以上的规则都没有关系了。 包括在html input 中输入字符都是浏览器以默认编码存储在内存中。