URI和URL的区别以及Javascript编码

本文将简单介绍一下URIURL之间的区别以及相应的使用场景。同时,我们将介绍一下unicode编码的概念,和对URL的编码混乱问题进行分析,如何通过Javascript等相关技术来解决编码统一的问题。主要用于知识点记录收集和学习markdown文件书写之用。


URI和URL

基本概念

URI和URL之间的区别,我之前一个博客中稍微了解了一下(@Just Happy)。
URI,是uniform resource identifier,统一资源标识符,用来唯一的标识一个资源。而URL是uniform resource locator,统一资源定位器,它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate这个资源。 也就是说,URI是以一种抽象的,高层次概念定义统一资源标识,而URL和URN则是具体的资源标识的方式。URL是一种URI 。

使用场景举例

String HttpServletRequest.getRequestURI();和StringBuffer HttpServletRequest.getRequestURL();返回的内容有何不同?为什么会如此?

在Java的URI中,一个URI实例可以代表绝对的,也可以是相对的,只要它符合URI的语法规则。而URL类则不仅符合语义,还包含了定位该资源的信息,因此它不能是相对的,schema必须被指定。

从HttpServletRequest的javadoc中可以看出, getRequestURI 返回一个

编码问题

关于URL的编码问题,我主要是从参考学习了(@阮一峰)的博客的相关文章。先将主要内容记录如下。

基本概念
这个是“字符编码笔记”一文中的内容。

ASCII
上个世纪60年代,美国制定了一套字符编码,对英语字符与二进制位之间的关系,做了统一规定。这被称为ASCII码,一直沿用至今。ASCII码一共规定了128个字符的编码,比如空格”SPACE”是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的1位统一规定为0。

非ASCII编码
英语用128个符号编码就够了,但是用来表示其他语言,128个符号是不够的。比如,在法语中,字母上方有注音符号,它就无法用ASCII码表示。于是,一些欧洲国家就决定,利用字节中闲置的最高位编入新的符号。比如,法语中的é的编码为130(二进制10000010)。这样一来,这些欧洲国家使用的编码体系,可以表示最多256个符号。

但是,这里又出现了新的问题。不同的国家有不同的字母,因此,哪怕它们都使用256个符号的编码方式,代表的字母却不一样。比如,130在法语编码中代表了é,在希伯来语编码中却代表了字母Gimel (ג),在俄语编码中又会代表另一个符号。但是不管怎样,所有这些编码方式中,0–127表示的符号是一样的,不一样的只是128–255的这一段。

至于亚洲国家的文字,使用的符号就更多了,汉字就多达10万左右。一个字节只能表示256种符号,肯定是不够的,就必须使用多个字节表达一个符号。比如,简体中文常见的编码方式是GB2312,使用两个字节表示一个汉字,所以理论上最多可以表示256x256=65536个符号。

Unicode
正如上一节所说,世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。

可以想象,如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。这就是Unicode,就像它的名字所表示的,这是一种所有符号的编码。Unicode的学名是’Universal Multiple-Octet Coded Character Set’,通用多八位编码字符集,Unicode是基于通用字符集(Universal Character Set)的标准来发展的,简称为UCS。

Unicode当然是一个很大的集合,现在的规模可以容纳100多万个符号。每个符号的编码都不一样,比如,\U0041表示英语的大写字母A,\U6CE2表示汉字”波”。具体的符号对应表,可以查询unicode.org,或者专门的汉字对应表

注意:
1. Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。
2. 如何才能区别Unicode和ASCII?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?
3. 英文字母只用一个字节表示就够了,如果Unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费。该如何存储呢?
出现了Unicode的多种存储方式,也就是说有许多种不同的二进制格式,可以用来表示Unicode.

UTF-8与Unicode的转换
UTF-8就是在互联网上使用最广的一种Unicode的实现方式。其他实现方式还包括UTF-16(字符用两个字节或四个字节表示)和UTF-32(字符用四个字节表示),不过在互联网上基本不用。

特点:它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
编码规则:
1.对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
2.对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的位数,用unicode码从低位往高位填充。如下:

Unicode符号范围UTF-8编码
0000 0000 到 0000 007F0xxxxxxx (和ASCII一致,一个字符占1个字节)
0000 0080 到 0000 07FF110xxxxx 10xxxxxx (一个字符占2个字节)
0000 0800 到 0000 FFFF1110xxxx 10xxxxxx 10xxxxxx (一个字符占3个字节存储)
0001 0000 到 0010 FFFF11110xxx 10xxxxxx 10xxxxxx 10xxxxxx (一个字符占4个字节存储)

事例:

“波” 的unicode码为 6CE2 ,表示成二进制映射码为 0110 1100 1110 0010 。
6CE2 在第3个范围,表示”波”这个字符占三个字节,同时将 0110 1100 1110 0010 从低位到高位填充到 1110xxxx 10xxxxxx 10xxxxxx
中,形成 1110 0110 1011 0011 1010 0010 的UTF-8编码,转换成十六进制为 E6B3A2 。

补充:

GB2312

1) GB2312又称国标码,由国家标准总局发布,1981年5月1日实施,通行于大陆。新加坡等地也使用此编码。它是一个简化字的编码规范,当然也包括其他的符号、字母、日文假名等,共7445个图形字符,其中汉字占6763个。我们平时说6768个汉字,实际上里边有5个编码为空白,所以总共有6763个汉字。
2) GB2312规定“对任意一个图形字符都采用两个字节表示,每个字节均采用七位编码表示”,习惯上称第一个字节为“高字节”,第二个字节为“低字节” 。GB2312中汉字的编码范围为,第一字节0xB0-0xF7(对应十进制为176-247),第二个字节0xA0-0xFE(对应十进制为160-254)。
GB2312将代码表分为94个区,对应第一字节(0xa1-0xfe);每个区94个位(0xa1-0xfe),对应第二字节,两个字节的值分别为区号值和位号值加32(2OH),因此也称为区位码。01-09区为符号、数字区,16-87区为汉字区(0xb0-0xf7),10-15区、88-94区是有待进一步标准化的空白区。

Big5

Big5又称大五码,主要为香港与台湾使用,即是一个繁体字编码。每个汉字由两个字节构成,第一个字节的范围从0X81-0XFE(即129-255),共126种。第二个字节的范围不连续,分别为0X40-0X7E(即64-126),0XA1-0XFE(即161-254),共157种。

GBK

GBK是GB2312的扩展,是向上兼容的,因此GB2312中的汉字的编码与GBK中汉字的相同。另外,GBK中还包含繁体字的编码,它与Big5编码之间的关系我还没有弄明白,好像是不一致的。GBK中每个汉字仍然包含两个字节,第一个字节的范围是0x81-0xFE(即129-254),第二个字节的范围是0x40-0xFE(即64-254)。GBK中有码位23940个,包含汉字21003个。

其他

国际标准化组织通过了一套ISO-8859-1的编码,规定了单字节256个符号的编码方式。目前,这是8位编码的国际标准。 Unicode编码中表示字节排列顺序的那个文件头,叫做BOM(byte-order mark),FFFE和FEFF就是不同的BOM。 UTF-8文件的BOM是“EF BB BF”,但是UTF-8的字节顺序是不变的,因此这个文件头实际上不起作用。有一些编程语言是ISO-8859-1编码,所以如果用UTF-8针对这些语言编程序,就必须去掉BOM,即保存成“UTF-8—无BOM”的格式才可以,PHP语言就是这样。

总结

本章节,主要说明了字符集的基本概念,以及unicode与utf8等编码之间的转换问题。详细说明了一个字符,计算是如何识别所占字节数和如何转码存储的。


URL编码

以下的部分内容摘自(@阮一峰)的博客文章“URL编码”一文和其他作者的,具体内容如下:

根据网络标准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只能使用英文字母、阿拉伯数字和某些标点符号,不能使用其他文字和符号。也可查看rfc3986关于URI的说明。

注意:这意味着,如果URL中有汉字,就必须编码后使用。但是麻烦的是,RFC 1738没有规定具体的编码方法, 而是交给应用程序(浏览器)自己决定 (不同的浏览器不一样,但现在貌似都采用utf8)。这导致”URL编码”成为了一个混乱的领域。

为什么要对URL编码?
1. 通常如果一样东西需要编码,说明这样东西并不适合传输 。原因多种多样,如Size过大,包含隐私数据, 对于Url来说,之所以要进行编码,是因为Url中有些字符会引起歧义 。
2. Url的编码格式采用的是ASCII码,而不是Unicode,这也就是说你不能在Url中包含任何非ASCII字符,例如中文。否则如果客户端浏览器和服务端浏览器支持的字符集不同的情况下,中文可能会造成问题。

哪些字符需要编码?
在RFC1738或RFC3986中有详细的说明。

如何编码?
Url编码通常也被称为百分号编码(Url Encoding,also known as percent-encoding),是因为它的编码方式非常简单,使用%百分号加上两位的字符(0123456789ABCDEF),代表一个字节的十六进制形式。Url编码默认使用的字符集是US-ASCII。例如a在US-ASCII码中对应的字节是0×61,那么Url编码之后得到的就是%61,我们在地址栏上输入http://g.cn/search?q=%61%62%63,实际上就等同于在google上搜索abc了。又如@符号在ASCII字符集中对应的字节为0×40,经过Url编码之后得到的是%40。

对于非ASCII字符,需要使用ASCII字符集的超集进行编码得到相应的字节,然后对每个字节执行百分号编码 。 对于Unicode字符,RFC文档建议使用utf-8对其进行编码得到相应的字节,然后对每个字节执行百分号编码 。 如前面提到的中文”波”使用UTF-8字符集得到的字节为0xE6 0xB3 0×A2,经过Url编码之后得到”%E6%B3%A2”。

如果某个字节对应着ASCII字符集中的某个非保留字符,则此字节无需使用百分号表示。例如”Url编码”,使用UTF-8编码得到的字节是0×55 0×72 0x6C 0xE7 0xBC 0×96 0xE7 0xA0 0×81,由于前三个字节对应着ASCII中的非保留字符”Url”,因此这三个字节可以用非保留字符”Url”表示。最终的Url编码可以简化成”Url%E7%BC%96%E7%A0%81″ ,当然,如果你用”%55%72%6C%E7%BC%96%E7%A0%81″也是可以的。

由于历史的原因,有一些Url编码实现并不完全遵循这样的原则,下面会提到。

Javascript函数统一编码

  1. escape(unescape)
    安全字符:*/@+-._0-9a-zA-Z(69个)

  2. encodeURI(decodeURI)
    安全字符:!#$&’()*+,/:;=?@-._~0-9a-zA-Z(82个)

  3. encodeURIComponent(decodeURIComponent)
    安全字符:!’()*-._~0-9a-zA-Z(71个)

兼容性不同:escape函数是从Javascript1.0的时候就存在了,其他两个函数是在Javascript1.5才引入的。但是由于Javascript1.5已经非常普及了,所以实际上使用encodeURI和encodeURIComponent并不会有什么兼容性问题。

code字符的编码方式不同 :这三个函数对于ASCII字符的编码方式相同,均是使用百分号+两位十六进制字符来表示。但是对于Unicode字符,escape的编码方式是%uxxxx,其中的xxxx是用来表示unicode字符的4位十六进制字符。这种方式已经被W3C废弃了。但是在ECMA-262标准中仍然保留着escape的这种编码语法。 encodeURI和encodeURIComponent则使用UTF-8对非ASCII字符进行编码,然后再进行百分号编码。这是RFC推荐的(故encodeURI和encodeURIComponent和页面的编码无关)。因此建议尽可能的使用这两个函数替代escape进行编码。 例如,escape(“波”)=”%u6CE2”(是unicode表示);encodeURI(“波”)=”%E6%B3%A2”(是十六进制表示)

对于包含中文的Url的处理问题,不同浏览器有不同的表现。例如对于IE,如果你勾选了高级设置”总是以UTF-8发送Url”, 那么Url中的路径部分的中文会使用UTF-8进行Url编码之后发送给服务端,而查询参数中的中文部分使用系统默认字符集进行Url编码 。为了保证最大互操作性,建议所有放到Url中的组件全部显式指定某个字符集进行Url编码,而不依赖于浏览器的默认实现。

编码出现中文的场合?
1. http://zh.wikipedia.org/wiki/春节 ,”春节”作为网址路径的一部分
2. 查询参数query string中, http://www.baidu.com/s?wd=春节
3. get方法提交表单,生成的URL
4. Ajax中使用的URL
其中1,2是作为网址,放在浏览器地址栏直接访问的,所以此时,还不知道页面指定的编码信息。3,4是通过发出请求,产生的URL,发送给服务器。

case1:网址路径的编码,用的是utf-8编码,是浏览器的默认编码。
case2:”春节”这两个字此时属于查询字符串,不属于网址路径,不要与case1混淆。用的是操作系统的默认编码。但貌似现在的浏览器已经做了设定,会在地址栏直接显示中文,但提交服务的时候,还是经过编码的。

前面说的是直接输入网址的情况,但是更常见的情况是,在已打开的网页上,直接用Get或Post方法发出HTTP请求。

case3:(参考GET POST 区别详解
GET方法的编码,默认取决于浏览器,可以通过js的encoeURI方法统一。
POST方法(形成form体,与get不同)的编码,用的是网页的编码。也就是由HTML源码中字符集的设定决定的
<meta http-equiv="Content-Type" content="text/html;charset=xxxx">。同时也指定浏览器渲染该文档所用的编码。

前面三种情况都是由浏览器发出HTTP请求,最后一种情况则是由Javascript生成HTTP请求,也就是Ajax调用。

case4
‘url = url + “?q=” +document.myform.elements[0].value;’
‘http_request.open(‘GET’, url, true);’
也是取决于浏览器的编码的。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值