CODEPAGE=936和65001两种情况下UrlEncode和URLDecode算法详解

  做网站的人都知道Server.UrlEncode函数,不过,网站做多了就会接触到gb2312和utf-8这两种不同的网页编码。网页编码只是最后形成的网页中对字符怎样编码,在网页形成的过程中还对应着不同的编码方案。一般而言,如果做的网页是gb2312的,那么ASP的CODEPAGE是936,而utf-8则是650001。
  UrlEncode虽然并不直接将汉字转化为内码,但与内码有着对应关系,就同样的汉字,比如“一级棒”,在CODEPAGE=936时被转化为“%D2%BB%BC%B6%B0%F4”,而CODEPAGE=65001时被转化为“%E4%B8%80%E7%BA%A7%E6%A3%92”。它们分别就对应了“一级棒”三字的GB码和utf-8码(utf-8码与UNICODE码有一一对应的关系)。
  在ASP中有两个函数可以分别获取汉字的GB和UNICODE这两种编码,它们分别是“Asc()”和“Ascw()”函数,不过这里要强列说明的是,在CODEPAGE=65001时“Asc()”函数会对双字节字符失效。
  下面就拿“一级棒”的“级”字为例,它的GB码为48310(-17226 + 65536)、UNICODE码为32423,转化为16进制,就分别为BCB6和7EA7。其中GB码的十六进制表示一眼就看出来,跟UrlEncode的结果是一致的,所以下面就只要谈谈utf-8了。
  进六进制7EA7在“0000 0800-0000 FFFF”范围内,对应的utf-8码为三字节。先将7EA7转化为二进制01111110 10100111,然后重新分段:0111 111010 100111,套用“1110xxxx 10xxxxxx 10xxxxxx”模板后得到11100111 10111010 10100111,就得到了E7 BA A7。再看一下UrlEncode的结果是否相符?
  如果要自己写UrlEncode或UrlDecode函数,则可以跟据以上这些规则来实现,下面先列出算法,然后再来讨论是否有必要写这样的函数。(友情提醒:为了排版效果,本站使用两个全角空格来代替一个Tab,如果要验证以下函数,请自行进行替换。)
Function MyUrlEncodeGB(char)
  Dim code, codeh, codel
  code = "0000" & Hex(Asc(char))'兼容英文字母与数字
  codel = Right(code, 2)
  codeh = Left(Right(code, 4), 2)
  
  '以上三行也可以用以下四行代替,功能完全一样,只是不同的习惯
  'code = Asc(char)
  'If (code < 0) Then code = code + &h10000
  'codeh = Right("0" & Hex(Int(code / &h100)), 2)
  'codel = Right("0" & Hex(code Mod &h100), 2)
  
  If (codeh <> "00") Then MyUrlEncodeGB = "%" & codeh
  MyUrlEncodeGB = MyUrlEncodeGB & "%" & codel
End Function
Function MyUrlEncodeUtf8(char)
  Dim code, codeh, codel, ucode1, ucode2, ucode3
  code = Ascw(char)
  codeh = Int(code / &h100)
  codel = code Mod &h100
  
  If (codeh = 0) Then MyUrlEncodeUtf8 = "%" & Right("0" & Hex(codel), 2) : Exit Function
  ucode1 = Right("0" & Hex(&hE0 + Int(codeh / &h10)), 2)
  ucode2 = Right("0" & Hex(&h80 + (codeh Mod &h10) * 4 + Int(codel / &h40)), 2)
  ucode3 = Right("0" & Hex(&h80 + codel Mod &h40), 2)
  MyUrlEncodeUtf8 = "%" & ucode1 & "%" & ucode2 & "%" & ucode3
End Function
Function MyUrlDecodeGB(code)
  Dim codeh, codel
  codeh = CInt("&h" & Mid(code, 2, 2))
  codel = CInt("&h" & Right(code, 2))
  MyUrlDecodeGB = Chr(codeh * &h100 + codel)
End Function
Function MyUrlDecodeUtf8(code)
  Dim ucode1, ucode2, ucode3, codeh, codel
  ucode1 = CInt("&h" & Mid(code, 2, 2)) - &hE0
  ucode2 = CInt("&h" & Mid(code, 5, 2)) - &h80
  ucode3 = CInt("&h" & Right(code, 2)) - &h80
  codeh = (ucode1 * &h10) + int(ucode2 / &h4)
  codel = ((ucode2 mod 4) * &h40) + ucode3
  MyUrlDecodeUtf8 = Chrw(codeh * 256 + codel)
  '如果只是用于显示,也可以使用以下这行代码
  'MyUrlDecodeUtf8 = "&#" & codeh * 256 + codel & ";"
End Function
  网页中可以直接使用UrlEncode和Request.QueryString来达到以上效果,按理说并没有写这四个函数的必要,但是仅仅依靠它们有时显得有些不足,比如你希望与另一个网站进行对接,而另一个网站的编码与你的网站不一样,那么以上方法就失效了。特别是Decode,并不是所有情况下都可以用Request.QueryString的,比如你要分析别人从哪个搜索引擎搜了什么关键词。
  还有几点要说明:
  一、如果你实在不想用MyUrlEncode**函数,那也有办法,ASP里可以用“Session.CodePage = "936"”和“Session.CodePage = "65001"”来临时转化代码。如:
Session.CodePage = "936"
Response.write(Server.UrlEncode("一级棒"))
Session.CodePage = "65001"
Response.write(Server.UrlEncode("一级棒"))
  友情提醒:在临时转化编码后,不要使用Response.Write来输出汉字。不信你试试
  二、在CODEPAGE=936和CODEPAGE=65001的编码体系中,Ascw和Chrw函数获得一致的结果。但是Asc和Chr在CODEPAGE=65001时得不到你要的结果。所以,严格说来,以上四个函数要同时使用,你得把它们放在CODEPAGE=936的ASP中。网上可以搜到很多UrlDecode函数的算法,它们基本都是同一个版本。其中存在的错误是没有解决utf-8格式的的URL。而一旦考虑到utf-8,就有可能在CODEPAGE=65001的编码系统中使用,可惜的是,这样又会损失原来对GB的支持。万能的办法是在CODEPAGE=936(网页编码GB2312)下使用,或者用以上第一点提到的办法临时换代码。
  三、字符串在内存里始终是UNICODE编码,只有用Response.Write输出到网页时才会按照CODEPAGE来进行转码,所以,对刚才第一点的“友情提醒”进行如下补充:如果你实在要输出汉字,先把它存到变量中,最后再Response.Write。
  四、编码只在用户交互的环节起作用:比如从Url或Form接收输入和将结果输出到网页。而在服务器本身处理的环节中不体现编码这回事,典型的是数据库操作。无论你是什么编码的网页,写进数据库的都是UNICODE,从数据库读出来的也都是UNICODE,所以你的程序可以任意在936(成生GB2312网页)和650001(成生utf-8网页)之间切换,甚至切换到BIG5都无所谓。本人的一级棒网站“www.eachfun.com”以前是基于GB2312的,3月29日临晨整体转换到utf-8,其实只改了程序而已,数据库还是那个数据库。

你可能感兴趣的:(asp专辑)