关于程序员:程序员都应该知道的URI一文帮你全面了解

49次阅读

共计 12773 个字符,预计需要花费 32 分钟才能阅读完成。

URI 是每个程序员都应该理解的概念,同时相关联的还有 URL, URN 等概念簇。理解这些概念,能够帮忙咱们更好地窥探万维网 (WWW) 的设计,同时也能帮咱们在工作中无效解决跟 URI 相干概念的问题,更加了解 encode,decode 工作原理,更好地助力网络编程!

1.URI

URI(Uniform Resource Identifier) , 意为对立资源标识符,提供了一套简略可扩大的形式对资源进行标识。

1.1 URI 的前世今生

为什么会有 URI?
随着万维网的倒退,须要有各种不同类型的资源被在网络上查找以及传输。因而,也就须要一种惟一的可在万维网上流传的标识,这样的对立资源标识就称为 URI。当然,资源在这里是一种抽象概念,或者抽象概念,能够泛指能够被标识的实体,就像一个网页,一本 e -book, 一份 pdf 等等,只有有须要被出现或者传输,都能够称为一种资源。

万维网奠基人 Tim Berners-Lee 对于超文本 (hypertext) 的提案中间接提出了用来标识超链接的想法–URL(Uniform Resource Locator)。因而,URL 也就最早被用来进行网络上能够提供拜访的地址示意。随着 HTTP, HTML 以及浏览器的逐渐倒退,越来越须要把标识资源可拜访地址以及单出命名示意资源这两种形式离开,因而也就提出了 URN(Uniform Resource Name),并用来示意后者。

IETF(网络工程工作小组)次要负责 URI 相干规范制订。

  • 1994 年公布 RFC1630, 指出了 URL 和 URN 的存在,同时定义了 URI 的正式语法。
  • 94 年 12 月,RFC1738 正式提出了 absolute 和 relative URL, RFC2141 则补充了 URN 相干的文法和语法定义。
  • 1999 年的 RFC2732 容许 URI 应用 IPv6 地址
  • 在 2005 年公布的 RFC3986 规范,解决了上述规范提出的一些短板,同时标记着 URI 通用语法正式称为官网互联网协议
  • RFC3305 规范指出,尽管 URL 名词被宽泛应用,然而其自身可能被逐步废除,并且只用来做为一些 URI 作为间接提供该资源拜访地址的提醒。并且指出资源标识符不须要示意该资源通过网络的拜访地址,或者基本不须要隐含该资源是基于网络提供的。(这里相当矛盾,其实 URL 曾经作为民间事实标准并被宽泛应用,也不是规范想颠覆就能立即颠覆的 – -)

1.2 URI 和 URL,URN 比拟

理解到 URI 和 URL,URN 整体的历史,能够看进去最早 URI 和 URL 其实是一脉相源的。起初为了兼容单纯通过命名或者名称来标识某个资源(并不是可被网络间接访达或者蕴含蕴含网络拜访地址)的状况,提出了 URN 规范。由此可见,这三个名称都能够示意对一项资源的定位标识。比拟有意思的问题是,在平时的工作沟通中,如何辨别,并且在什么样的场景下该应用哪个名称?

1.2.1 基本概念

先具体理解每个名称的基本概念:
1.URI
对立资源标识符。
用来示意某个特定资源。设计进去能够进行任何实体或者非实体的标识,然而目前被常常用于在网络上可传输内容的标识。URI 是由一串特定字符集的字符组成,并且由 IETF 制订的规范定义了一组语法规定,用来保障某个资源的对立和惟一标识。

2.URL
对立资源定位符。
也能够被称为网络地址。在万维网上,每个资源都有能够有惟一地址指向该资源,同时,通过该地址能够进行资源的读写,这样的地址标识就称为 URL。URL 蕴含了目前网络上常见的格局,包含 web 站点地址 http, 文件传输协定 ftp, emal 地址协定 mailto 以及数据库拜访地址 JDBC 等。

3.URN
对立资源名称。
URN 用来通过名称标识在特定命名空间的某个资源,同时心愿为资源能够提供一种较长久的,与地位和存取形式无关的示意形式。URN 并不关注这个示意名称里是否隐含了该资源的地位,或者如何获取它,也不肯定代表该资源肯定可用。
举个例子 ,在 ISBN(Internal Standard Book Number) 零碎中,一个编号(相似 9971-5-0210-0)代表了一个书本资源,该编号在 URN 中能够示意为 urn:isbn:9971-5-0210-0, 然而这个编号并没有给出在哪里或者如何找到这本书的信息,它只能惟一标识了这本书。

1.2.1 三者之间的关系

先上图来阐明 URI,URL 和 URN 之间的关系。

URI 能够认为是一个形象的概念,所有的 URL 以及 URN 都是 URI。RFC3986 规范中有这样一段:

A URI can be further classified as a locator, a name, or both. The term“Uniform Resource Locator”(URL) refers to the subset of URIs that, in addition to identifying a resource, provide a means of locating the resource by describing its primary access mechanism (e.g., its network“location”).
rfc 3986, section 1.1.3

URI 能够被分类成 locator 或者对应的名称示意,也就是蕴含了 URL 和 URN 的概念。因而,平时咱们在说 URL 的时候,它其实也能够被称为 URI。

同样,这里有个十分有意思的问题,URN 其实比拟好辨别开,在应用惟一标识资源名称时能够应用,然而 URI 和 URL 如何辨别在哪个场景进行应用?
这个问题其实和 RFC3986 规范定义的不够分明无关,请再看上面这一段:

The URI itself only provides identification;access to the resource is neither guaranteed nor implied by the presence of a URI.

rfc 3986, section 1.2.2

URI 不保障提供该资源的拜访形式,或者隐含保障该资源是否存在(其实语义就是该 URI 就是一个名称示意),然而在上一段中又申明了 URI 会被分类成 name 或者 locator, 示意 URI 应该蕴含 locator 这种拜访形式。再看上面这一段:

Each URI begins with a scheme name, as defined in Section 3.1, that refers to a specification for assigning identifiers within that scheme.

rfc 3986, section 1.1.1

每个 URI 都须要蕴含有起始 scheme 名称。比方:https://www.example.com,这样的一串字符串就能够称为 URI,然而明确标识了应该如何去拜访这个资源,同时它也是 URL,因为 URL 是用来告知接管方获取该资源的形式。

IETF 在 RFC3986 中也有一段对于 URI 和 URL 应用形式的阐明:

Future specifications and related documentation should use the general term“URI”rather than the more restrictive terms“URL”and“URN”

rfc 3986, section 1.1.3

这样看来,如同 IETF 更反对应用 URI 来代替 URL 这个称说。然而思考到 URL 目前曾经成为用来形容网络上资源定位的事实名称,而且 RFC3986 曾经诞生超过 15 年了(有些条目的确跟不上时代倒退速度),所以在针对互联网资源定位(即网络地址)的时候,URL 能够算是更贴切的名称。当然,如果对方跟你谈 URI 等等,这也没问题,因为 URI 算是超类,并且也能够代表该资源。

上面是这个问题论断:

  • URI 是一种标记符
  • URL 是能够通知你如何去拜访或者获取该资源的一种标记符
  • 在形容网络资源地址的时候,用哪种都没问题,须要明确的准则就是最好和你的信息接管方用同样的称说,不便了解
  • 如果感觉不好拿捏属于 URL 或者 URN,那就能够间接应用 URI 形容

2.URI 字符集

2.1 URI 的设计点

URI 须要提供一种简略,可扩大的形式来惟一标识资源。同时,又须要思考到在不同媒介上进行流传的示意模式。因而,URI 在设计时须要思考到以下几点:

  • URI 须要是可移植的。

不同的零碎,或者不同的接管方之间都能够应用 URI 协定来标识资源。URI 能够被示意成多种形式,比如说在纸上书写的字符串,或者屏幕上的像素,或者一系列通过编码的二进制流等。URI 的解析只跟这些出现形式所关联的字符串无关,而跟具体表现形式,载体无关。
思考到 URI 更多须要在网络场景传输,因而:

  • URI 是由一串字符序列组成
  • URI 可能会从非网络环境中移植到网络环境下,然而网络环境的输出个别受制于键盘,鼠标等输出载体,因而最好由能够被这些物理载体不便输出的字符出现
  • URI 个别须要被人们记住并应用,所以这些字符最好是人们常常应用并且相熟的内容

基于上述思考,URI 为一串受限的字符所组成的字符串,并抉择 US-ASCII 作为字符集。US-ASCII 字符集基本上被所有零碎反对,而且兼容性良好,可能反对 URI 所须要的移植性。

  • URI 须要将标识和动作离开

这一层思维其实是须要将示意和体现离开。URI 只关注某个资源的标识,如果进行这个资源的存取或者拜访不做任何形式的保障。同资源相干的动作,援用等,在设计时被交给具体实现 URI 下 scheme 的协定来制订,例如,http 协定会具体关怀一个用’http’scheme 示意的资源如何进行’get’,‘update’,’delete’等一系列操作等。
这样能够保障 URI 协定的绝对稳固,以及比拟好的扩展性

  • 层级标识

因为资源常常具备层级关系,比方在一个 example.com 站点下可能会挂有多个资源,或者上面会有一个目录’dir’, 该目录下会蕴含多个资源,这就意味着 URI 须要有一种层级的组织形式。
在设计中也思考到了这样类型的资源组织形式,容许 URI 依照层级组织,并且在字符串上依照从左到右的程序拆分组件。
相似于罕用操作系统的文件系统一样,URI 能够用来还原具备层级关系的资源零碎的组织构造。

2.2 URI 所抉择的字符集

如上所属,URI 抉择 通过 US-ASCII 字符集来进行示意,并限度应用从其中所筛选的一部分字符,数字以及符号。而且,因为须要反对层级构造,以及 URI 本身蕴含了不同的局部,因而也须要保留一些字符用来做这些有语义的局部的分隔。

Note: 因为须要对字符集或者语法进行形容,下文都是用 IETF 应用的通用形容零碎 ABNF(Augmented Backus-Naur form), 即加强巴科斯范式。
加强巴科斯范式所定义的语法结构个别如下:

rule = definition / definition; comment CR LF
rule = *element

示意一组规定由一系列字符串组成的定义来形容,第一组 rule 通过’/‘来示意定义中’或者’的关系。如果该条规定须要减少正文,那么须要通过’;’ 来标识正文的开始
第二组 rule 示意反复规定,其中 a 标识起码反复次数,b 标识最多反复次数。例如,2*3element 标识 element 起码呈现两次,最多呈现三次

对于加强巴科斯范式的具体内容请参照:
https://en.wikipedia.org/wiki/Augmented_Backus%E2%80%93Naur_form

2.2.1 Percent-Encoding

因为 URI 在协定中只筛选了局部 ASCII 字符,数字以及符号,那么当须要示意不在这个范畴之内的符号,字符,或者该字符在 URI 中被用来分隔符等非凡用处时,就须要对这个字符进行 % 编码。百分号编码也能够叫做 URLEncode,其个别格局为:

pct-encoded = "%" HEXDIG HEXDIG 

将不能间接应用的字符先转为字节流示意(个别为 utf- 8 编码,须要具体看上下文和 URI scheme 协定制订),而后每个字节转换为 % 加两个十六进制字符来示意。例如:
“00101011”该字节须要编码为“%2B”, 在 ASCII 码表中示意为 “+” 号

Note: 百分号编码不关怀大小写,然而为了对立和统一,最好应该应用大写字符

2.2.2 Reserved Characters

URI 保留字符集。
URI 本身定义时蕴含了 components 以及 subcomponents,那么这些不同的 components 就须要通过分隔符来进行标识。这些被用来进行示意分隔的字符就成为保留字符集,这些字符集可能会被用作(或者未来会被用作)URI 不同局部的分隔符。
以下为 reserved character 所波及的字符集示意:

reserved    = gen-delims / sub-delims

gen-delims  = ":" / "/" / "?" / "#" / "[" / "]" / "@"

sub-delims  = "!" / "$" / "&" / "'"/"("/")"/"*"/"+"/","/";"/"=" 

gen-delims 字符集用来示意 URI component 之间的分隔符,思考到 component 内会由不同的 subcomponents 组成,因而须要 sub-delims 字符集来定义 subcomponents 之间的分隔符。

Note: 这些字符在 URI 中个别具备非凡语义,因而不能被编码。同时,如果在进行两个 URI 相等性比拟时,如果其中一个对协定中 component 局部不能编码的保留字符进行编码,即便解码后两个 URI 字符雷同,也会被认为是两个不同的 URI

2.2.3 Unreserved Characters

容许呈现在 URI 中,并且不会被拿来用作保留字符集的字符汇合成为 Unreserved Characters。所波及到字符 ABNF 示意为:

unreserved  = ALPHA / DIGIT / "-" / "." / "_" / "~"

ALPHA = a-z / A-Z

DIGIT = 0-9 

这些字符为非保留字符,在 URI 应用过程中是不须要进行编码的。

Note: 如果在 URI 比拟中蕴含这些字符,那么该字符自身或者其编码格局都应该认为是相等的,即这些字符编码不编码不会影响相等性。另外,这些字符在应用时最好不要编码,即便曾经被编码,那么在应用时也应该先对这些字符进行解码。

2.2.4 总结

一图来示意在 URI 中所波及到的保留和非保留字符,须要留神的是保留字符在不做分隔符或者具备非凡含意的时候是须要编码的。

3.URI Component

URI 语法规定由一系列 component 组成,并且在设计时须要思考到扩展性以及对各个资源定位类型的兼容,因而在其起始都会有一个 scheme 头来特定标识这个 URI 所定义的资源类型标识符。另外,URI 因为是所有资源类型的超集(会细分为 URL 和 URN),所以 URI 所波及的定义都是须要被恪守的根本定义。
URI component 个别由以下 component 组成(应用 ABNF 形容):

URI         = scheme ":" [//authority] path ["?" query] ["#" fragment]

authority   = [userinfo@] host [:port] 

Note:

schme 和 path 为 required

有了上述语法规定的定义,举个例子来阐明 URI 下两种不同的标识符所定义的各个 component 局部

下文将具体介绍各个组件局部,以及相应的语法规定。

3.1 URI component

3.1.1 Scheme

component

scheme

容许字符集

a-z  A-Z  0-9 + . –

是否 case-sensitive

component 完结标识符

:

Note:

  • 表中字符集为了出现清晰,因而正则中通过非必要空格进行分隔,并且表或者关系
  • 完结标识符示意语法解析时该 component 解析结束符

scheme 用来标识 URI 所对应的具体协定。每个 URI 都必须以 scheme 结尾。URI 的语法规定如下(应用 ABNF 形容):

scheme      = ALPHA *(ALPHA / DIGIT / "+" / "-" / ".") 

如上文所说,URI 定义通用的语法规定,scheme 所标识的具体协定会定义通用规定外的具体语法规定。例如,以 geo 为 scheme 的协定 URI,示意特定地理位置标识,其语法规定如下:

geo:<lat>,<lon>[<alt>][u=<uncertainty>] 

参考自 RFC 5870

URI scheme 的官网注册信息目前由 IANA(Internet Assigned Numbers Authority) 组织进行增加和保护,目前约蕴含了 335 种不同协定 scheme,具体可参考 https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml

3.1.2 Authority

component

Authority

component 开始标识符

//

component 完结标识符

/  ?  #

authority component 设计的目标为设定一个命名空间,并且标识这个命名空间被哪个机构所治理,例如 baidu.com, google.com 等等。authority 个别由三局部组成,蕴含了可选的 userinfo,port 以及必选的 host 局部。
对于为什么 Authority 局部会抉择 // 作为起始符号的起因,Tim Berners-Lee 曾答复过:

  1. 须要抉择一个命名零碎来进行资源的层级化命名,/ 作为 unix 零碎通用的分隔符能够在 URI 的设计中失去复用,因而应用 / 来作为 relative URI 的分隔符
  2. 须要有符号将 host 局部(相似 www.example.com)同 URI 的其余局部进行辨别,这部分设计参考了过后 Apollo domain system(其应用 //computername/file/path 进行命名)的设计形式
  3. 当初来看,他认为这个语法是比拟冗余的,更喜爱间接通过:来进行域名分隔,例如 http://www.example.com/foo/bar 转写为 http:www.example.com/foo/bar,这样写同样能够辨认到 server 并且更为简化

由此可见,规范的设计也是须要再一直地迭代和试验中后退 :)

3.1.2.1 Userinfo

component

Userinfo

容许字符集

pct-encode 字符集 unreserved 字符集 sub-delims 字符集  :

是否 case-sensitive

component 完结标识符

@

userinfo 蕴含了用户相干信息(个别为名称,新式格局 user:password 因为波及平安危险已被弃用),同时须要通过 @合乎和 host 进行分隔。Userinfo 局部的语法规定如下(应用 ABNF 形容):

userinfo    = *(unreserved / pct-encoded / sub-delims / ":") 
3.1.2.2 Host

component

Host

容许字符集

pct-encode 字符集 unreserved 字符集 sub-delims 字符集

是否 case-sensitive

component 完结标识符

/  :

服务提供商通过 host 来提供服务,同时基于 dns 域名解析,server 和 host 之间能够做到非一一对应。host 局部能够有三种示意形式,IPv6,IPv4 或者 registered name。registered name host 的语法规定如下(通过 ABNF 形容):

host        = IPv6address / IPv4address / reg-name

IPv6address = [HEXDIG *( :: HEXDIG) ]

IPv4address = DIGIT "." DIGIT "." DIGIT "." DIGIT

reg-name    = *(unreserved / pct-encoded / sub-delims) 
3.1.2.2 Port

component

Port

容许字符集

0-9

component 完结标识符

/

port 为可选项,同时通过十进制进行示意。在 URI 语法中,port 须要跟在 : 后。port 的语法规定如下(应用 ABNF 形容):

port        = *DIGIT 

每种 scheme 个别会定义一个默认端口。例如,http 定义 80 默认端口,https 定义 443 默认端口等。

3.1.3 Path

component

Path

容许字符集

pct-encode 字符集 unreserved 字符集 sub-delims 字符集  @  :

component 完结标识符

?#  EOF

path 标识了 host 下特定的资源门路,蕴含了一系列通过 / 分隔的 segments。须要留神的是,如果 URI 曾经蕴含了 authority 局部,那么 path 局部或者为空,或者须要以 / 来结尾。另外,URI 还容许 relative-path 的应用形式,这样的形式第一段 path segment 不能蕴含:(如果蕴含,会被 parser 认为是 authority 局部)。以下是简化的 path 语法规定(应用 ABNF 形容):

path          = path-abempty  /  path-relative

path-abempty  = *("/" segment)

path-relative = segment-nocolon *("/" segment)

segment       = *pchar

pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"

segment-nocolon = unreserved / pct-encoded / sub-delims / "@" 
3.1.4 Query

component

Query

容许字符集

pct-encode 字符集 unreserved 字符集 sub-delims 字符集  @  :

component 开始标识符

component 完结标识符

  EOF

query 局部提供了定位资源的辅助信息,query 其外部语法并没有明确定义,然而个别由 name-value 键值对组成的字符串组成,两头通过分隔符 & 进行分隔。例如:name1=value1&name2=value2。query 的语法规定如下(应用 ABNF 形容):

query       = *(pchar / "/" / "?")

pchar       = unreserved / pct-encoded / sub-delims / ":" / "@" 
3.1.5 Fragment

component

Query

容许字符集

pct-encode 字符集 unreserved 字符集 sub-delims 字符集  @  :  /  ?

component 开始标识符

component 完结标识符

EOF

fragment 为段落标识符,个别用来标识一个 resource 的特定局部(一个资源子集或者一部分,或者通过这个资源来形容的一些其余资源)。fragment 以 # 作为起始标识符,其语法规定如下(通过 ABNF 形容):

fragment    = *(pchar / "/" / "?")

pchar       = unreserved / pct-encoded / sub-delims / ":" / "@" 
3.1.6 小结

各个 component 容许的字符集局部是咱们须要特地关注的,须要留神在五个 component 之间容许应用 gen-delims 字符集,在每个 component 内(即小组件间)容许应用 sub-delims 字符集。

3.2 解析 URI

如何通过程序来解析 URI, 并失去 URI 各个 component?
如上一节 ABNF 语法规定形容,URI 满足上下文无关文法。因而,咱们能够通过语法图来出现整体 URI 的解析规定,如下:

有了上图,应用递归降落,解析的伪代码就十分好写了:

`/**

  • 读取下一个字符

**/
function next() {
skip space;
read next char and return;
}

/**

  • 预扫描,查看对应的 input 字符串是否蕴含有 special_char,
  • 以及其地位

**/
function contains(input, special_char) {
start = input.start, end = input.end;
while (start < end) then

 if special_char equals start then return;

end
return start
}

/**

  • 对 uri 的解析函数
  • 具体的解析 component 办法为 parse_*, 须要匹配的字符集以及语法规定可参照上文中各个 ABNF

**/
function parse(string uri) {
parse_scheme;
skip next ‘;’ ;
if next() == “//” then

   if contains(substring_uri(// until path), '@') then
      parse_userinfo;
   end
   parse_host;
   if next() == ':' then
      parse_port;
   end

end
parse_path;
if next() == ‘?’ then

   parse_query;

end
if next() == ‘#’ then

   parse_fragment;

end
}`

5. 再论 Encode 和 Decode

什么时候该 encode 或者 decode?
先说 URI 的设计目标,URI 被设计出并可在万维网上进行广泛传播,因而对各个子系统,浏览器等媒介的兼容性是最重要的,因而被设计应用被宽泛应用的 ASCII 码进行承载。
因而,在生成 URI 过程中,应该先实现各个 componet 局部的编码,而后在联结 gen-delimiter 拼接成 URI。因为各个 scheme 的具体协定不同,因而只有在生成 URI 的过程中,才能够晓得具体哪些 delimiter 会须要被编码,或者会被应用作为真正的 delimiter。一旦 URI 被生成,该 URI 在流传时就应该放弃其 百分号 encode 的格局。
当百分号编码的 URI 在解码时,应该先通过 gen-delimiter 以及 sub-delimiter 将各个 component 进行拆散,而后再对各个 component 进行别离解码 。这样能够保障依照生成的 URI 被残缺解码。
另外,须要留神的是,2.2.3 中提到的 unreserved 字符集能够在任意时刻被编码和解码,然而举荐在生成 URI 时不对这些字符集进行编码,同时在解码时应该优先对这些字符集的百分号编码格局进行解码。

Note: 不应该对同一个 URI 反复次编码或者解码,这样会导致 URI 所代表的语义生效。例如,对曾经进行百分号编码的 URI 再进行编码时,又会再次对其中的百分号进行二次编码,从而导致 URI 在进行解码时含意谬误。

5.1 实现 encode 和 decode

依照上文的说法,encode 须要先依据对应的 component 局部来组成不须要进行 escape(即不须要编码)字符的规定,而后再进行逐个的判断和编码,之后再将编码过后的 component 拼接称为 URI(当然,如果所有的 delimiter 都不须要进行编码,那能够间接对整个 URI 进行编码,不须要 escape 的字符集间接蕴含这些 delimiter 字符)。decode 则须要先将各个 component 依照 delimiter 进行拆分,而后别离对各个 component 在须要解码的字符规定下进行解码。

Note: 在标识 ASCII 以外的字符集时,个别是用 Unicode 字符集,编码方式为 UTF-8。
因而,在编码和解码过程中,如果编程语言层面应用 UTF-16 进行字符编码(相似于 Java 和 JavaScript), 那么须要将其转为 UTF- 8 编码,同时须要针对 UTF-16 带来的 surrogate pair 进行额定解决。
对于 surrogate pair 形容,能够参考
https://stackoverflow.com/questions/5903008/what-is-a-surrogate-pair-in-java#:~:text=The%20term%20%22surrogate%20pair%22%20refers,values%20between%200×0%20and%200x10FFFF.&text=This%20is%20done%20using%20pairs%20of%20code%20units%20known%20as%20surrogates.

5.1.1 encode

encode 的实现中须要留神的就是对须要编码的字节进行 % 编码,伪代码如下:

`/**

  • 对某一段 string s 进行 URI encode 编码
  • 传入 s 以及不须要编码的字符集 dontNeedEncodingSet, 返回 URI encode 后的 string

*

  • dontNeedEncodingSet 字符集须要依据 3.1 中的 component 形容来定,例如 Path 中的不须要编码字符集
  • 个别为 unreserved 字符集 sub-delims 字符集 @ :(sub-delims 字符集以及 @ : 如果其自身须要呈现在
  • component 中而不是用来做分隔语义,那么同样须要进行 encode),另外不同的语言实现在不须要编码字符集
  • 上可能会有不同的抉择

**/
function encode(s, dontNeedEncodingSet) {
// 申明 R 为后果字符串
def R, index = 0, strLen = s.length();
while index < strLen then

  def c 为 s 在 index 下的字符示意;
  if c 蕴含在 dontNeedEncodingSet 里 then
    R += c;
  else
    def 长期后果 out;
    /**
    * 这里须要思考如果是 utf-16 字符编码,那么须要判断 surrogate pair
    **/
    if c 在 surrogate pair 中的第一个字符所示意的范畴内 then
      def c2 为 ++index 地位字符;
      将 c c2 两个字符组成 utf-16 并进行 utf- 8 编码;
      将上述后果赋值给 out;
    else 
      如果 c 为 utf-16 编码,须要转为 utf- 8 编码;
      out = c;
    end
    // 外围百分号 encode
    取 out 中每一个字节 out_byte;
    R += '%' +((out_byte >> 4) & 0xF)转为 16 进制大写示意 +((out_byte) & 0xF)转为 16 进制大写示意;
  end
  ++index;

end
return R;
}`

5.1.2 decode

decode 的实现中须要留神在遇到 % 号时读取后续字符进行解码,同时如果语言实现应用 utf-16 编码那么须要对 surrogate pair 进行还原(这部分语言自身个别都提供办法来对 utf- 8 进行转换),伪代码如下:

`/**

  • 对 s 进行解码,返回解码后的 string

**/
function decode(s) {

// 申明 R 为后果 string
def R, index = 0, lenStr = s.length();
while index < lenStr then
    def c 为 s 在 index 下的字符示意;
    if c == '%' then
        def 两头长期后果 out;
        while c == '%' && index + 2 < lenStr then
            读取 index+1, index+2 字符 c1, c2;
            // 外围 decode
            out += (字符转为 hex 示意(c1)) << 4 | (字符转为 hex 示意(c2));
            index += 3;
        end
        // 异常情况报错
        if c == '%' && index < lenStr then 抛出谬误;
        // 留神:如果语言实现须要 utf-16 编码,那么须要后行将 out 转为 utf-16 编码
        R += out;
    else
        R += c;
        ++index;
    end
end
return R;

}`

5.1.3 小结

置信各位曾经对 URI 有了一个绝对全面的理解,在理论工作的应用中,还须要依据语言所提供的对应 encode,decode 办法文档来进一步理解其编解码所定义的 component 局部非凡保留字符,这样会对所应用语言提供的 encode/decode 有更深刻的理解:)
**
Enjoy your coding trip~

作者:王阳(好将来 Java 开发专家)

正文完
 0