URL是Universal Resource Locator的简称,翻译过来那就是统一资源定位符,好吧,我们经常会俗称为网页地址。一个URL的格式通常是这样的:协议类型://服务器地址(必要时需加上端口号)/路径/文件名?参数,比如http://zh.wikipedia.org:80/w/index.php?title=Special,协议是HTTP,服务器地址是zh.wikipedia.org,端口是80,路径和文件名是/w/index.php,参数是title=Special。还有个与URL相关的概念URI,URI是统一资源标示符,URL是URI的一种,用于标示互联网资源,并指定了对资源的操作和获取方法。URL大部分情况下都只有英文字符,这样也就不存在编码问题,如果URL中有了中文,那么编码规则是什么呢?实际上,RFC并没有标准规定URL的编码方式,所以不同的浏览器的表现可能是不同的,下面就总结下。主要参考了阮一峰的关于URL编码一文,当然有些地方我测试的结果会有所不同,大家可以根据自己的系统环境进行区分。
如果URL路径中包含中文,经过测试发现不管IE6.0还是Chrome,编码都是采用的UTF-8。其他浏览器暂时没有测试,猜想应该一致。
测试的URL是:http://zh.wikipedia.org/wiki/中文.在chrome的开发者工具中可以看到实际的URL是http://zh.wikipedia.org/wiki/%E4%B8%AD%E6%96%87,其中E4B8AD E69687正是"中文"的UTF-8编码。
如果查询参数中包含中文,则经过测试发现,IE6.0采用的是操作系统编码,Chrome采用的是UTF-8编码。
测试的URL是:http://www.baidu.com/s?wd=中文。通过测试可以发现,在IE6下面“中文”实际被转成了%B0%D9%B6%C8,chrome下面则转成了%E4%B8%AD%E6%96%87。
表单提交时,不管是IE6还是Chrome,参数中中文的编码则根据HTML代码中指定的字符编码来决定(也就是html代码中标签指定的字符编码)。当然这是在form中没有指定accept-charset的情况下,如果form中加了accept-charset="GBK”属性,则表单参数则由accept-charset指定编码进行编码。
测试代码如下:
<html> <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <title>测试</title> </head> <body> <h2>表单中文编码测试</h2> <form method="GET" action="http://www.baidu.com/s"> <input type="text" name="wd"> <input type="submit" value="OK"> </form> </body> </html>
可以发现,当指定charset=UTF-8时,你在输入项输入“中文”,则实际提交后会用UTF-8编码成%E4%B8%AD%E6%96%87,而如果charset=GBK,则输入项会用GBK编码成%D6%D0%CE%C4。用POST方法也是一样的。
而如果加上accept-charset属性,代码改成下面这样,则表单参数编码由accept-charset中指定的编码来决定。如下所示,虽然meta中指定的是UTF-8编码,但是表单参数是GBK编码的。
<html> <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <title>测试</title> </head> <body> <h2>表单中文编码测试</h2> <form method="GET" action="http://www.baidu.com/s" accept-charset="GBK"> <input type="text" name="wd"> <input type="submit" value="OK"> </form> </body> </html>还有一点需要注意的是,如果输入项包含空格,比如“中文 哈哈”,则空格会被编码为+。
之前说的是直接通过浏览器发HTTP请求的中文编码情况,那如果是通过Javascript发送HTTP请求,会是什么情况呢?根据测试发现,通过Javascript发送的HTTP请求,IE6的为操作系统编码,Chrome中文参数编码是UTF-8。
测试:可以打开Chrome的http://zh.wikipedia.org/wiki/%E4%B8%AD%E6%96%87,在开发者工具的控制台输入$.ajax("/wiki/英语"),可以在网络连接中发现"英语"采用的UTF-8编码。IE中可以另外编辑一个测试的网页,用JS测试即可。
前面说的很多种中文编码情况,不同的浏览器处理方式也不尽相同,这是件非常纠结的事情,一个好的方法是采用JS的函数在表单提交前对参数进行统一处理。
第一个函数是escape,escape是一个全局函数,它使用十六进制的数字(%xx或%uxxxx)编码字符串为unicode码。小于等于0xFF的字符将被转义为%xx,大于0xFF的将被转移为%uxxxx,可以使用unescape函数解码escape函数编码的字符串。escape已被ecma标准抛弃,现在一般推荐使用encodeURI或encodeURIComponent函数代替。
escape实例: escape(“中文”)的结果是"%u4E2D%u6587",escape("abc def")的结果是"abc%20def",空格编码为0x20。无论网页编码是什么,经过JS的escape操作后,都会变成unicode码。如第3节提到的,由于表单提交时参数中的空格会被编码为+,所以escape函数不对"+"进行编码,所以escape("abc+def")结果还是"abc+def"。
第二个函数是encodeURI,它也是全局函数,encodeURI的目的是采用UTF-8给URI进行编码。ASCII的字母、数字不编码,- _ . ! ~ * ' ( )也不编码,URI中具有特殊意义的字符也不编码(如; / ? : @ & = + $ , #等)。参数中的其他字符将转换成UTF-8编码方式的字符,并使用十六进制转义序列(%xx)生成替换。对应的解码函数是decodeURI。
encodeURI实例:encodeURI("测试 http://www.baidu.com/test?v=ab cd+@#")结果为"%E6%B5%8B%E8%AF%95%20http://www.baidu.com/test?v=ab%20cd+@#"。
第三个函数是encodeURIComponent,与encodeURI不同的是,它会对特殊符号如"; / ? : @ & = + $ , #"进行编码。解码函数是decodeURIComponent。
encodeURIComponent实例:encodeURIComponent("测试 http://www.baidu.com/test?v=ab cd+@#")结果为%E6%B5%8B%E8%AF%95%20http%3A%2F%2Fwww.baidu.com%2Ftest%3Fv%3Dab%20cd%40%23%2B。