如何对ASCII字符进行URL编码?

7
我将使用Ruby提取要下载的文件的URL并下载它。文件名包含utf8字符,例如:

www.domain.com/.../ÖÇÄÜ360ÓïÒôÖúÀí.txt

尝试下载上述URL时失败。使用URI::escape生成的URI也无法正常工作:
www.domain.com/.../%C3%96%C3%87%C3%84%C3%9C360%C3%93%C3%AF%C3%92%C3%B4%C3%96%C3%BA%C3%80%C3%AD.txt

但是如果我按照URL编码参考进行操作,它就可以正常工作:

www.domain.com/.../%D6%C7%C4%DC360%D3%EF%D2%F4%D6%FA%C0%ED.txt

我试图在Ruby中搜索一个与此完全相同的编码函数,但我没有找到任何信息。在我尝试编写一个实现上面链接中表格的函数之前,我想问一下是否有任何现有的库可以做到这一点。如果我决定这样做,应该对哪些字符范围进行编码,显然不是所有内容。

我正在使用JRuby 1.6.2和RUBY_VERSION => "1.8.7"


1
字节C3 96是UTF-8编码的Ö。在ASCII中,相同的字符表示为单个字节D6。因此,解决问题的一种方法是将UTF-8字符转换为ASCII(在可能的情况下),然后进行URI::escape。但是,对于没有ASCII等价物的Unicode字符,这并没有帮助。 - David Gorsline
2
那是http://w3fools.com/,你应该避免使用它们。那个编码表正在使用[ISO 8859-1](http://en.wikipedia.org/wiki/ISO-8859-1),你也不应该使用它,而应该使用UTF-8。如果你需要Latin-1,那么你必须将你的UTF-8单独转换为Latin-1。 - mu is too short
2
@muistooshort,情况比那更糟——表格是Windows 1252编码。 - matt
我同意,但是我正在处理的第三方服务使用那种编码方式,而我无法更改。在同一个w3school页面上,有两个例子,第二个例子演示了javascript编码函数的结果与URI.escape相同。但是如果您尝试第一个例子,它演示了浏览器在将URL发送到服务器之前如何对其进行编码,并且它使用了我正在寻找的相同编码,该编码由我一直参考的表格表示。当我看到浏览器以相同的方式对此进行URL编码时,我开始相信这是一种正确的方法。 - Rami
@matt:没错,看起来像是CP1252字符集,‰符号就在其中。那些傻瓜把它们称作ASCII字符集,这让我们有更多的理由远离它们。 - mu is too short
显示剩余4条评论
1个回答

15

字符编码的麻烦之处在于这里。

发生的情况如下。Ruby内部将你提取的字符串存储为字节序列,即文件名的utf-8编码。当你对其调用URI.escape时,这些字节会以%xy格式进行转义,而结果字符串现在仅由ASCII范围内的字节组成,并用作URL。

然而,接收服务器会解释这些字节(从%xy形式中进行反转义),就好像它们是另一种编码,例如ISO-8859-1,因此它得出的文件名与它拥有的任何内容都不匹配。

以下是一个演示,使用Ruby 1.9,因为它对编码的支持更好。

1.9.3-p194 :003 > f
 => "ÖÇÄÜ360ÓïÒôÖúÀí.txt" 
1.9.3-p194 :004 > f.encoding
 => #<Encoding:UTF-8> 
1.9.3-p194 :005 > URI.escape f
 => "%C3%96%C3%87%C3%84%C3%9C360%C3%93%C3%AF%C3%92%C3%B4%C3%96%C3%BA%C3%80%C3%AD.txt" 
1.9.3-p194 :006 > g = f.encode 'iso-8859-1'
 => "\xD6\xC7\xC4\xDC360\xD3\xEF\xD2\xF4\xD6\xFA\xC0\xED.txt" 
1.9.3-p194 :007 > g.encoding
 => #<Encoding:ISO-8859-1> 
1.9.3-p194 :008 > URI.escape g
 => "%D6%C7%C4%DC360%D3%EF%D2%F4%D6%FA%C0%ED.txt"

因此,在这种情况下,解决方案是在转义之前将字符串编码为ISO-8859-1。在Ruby 1.9中,您可以像上面那样执行此操作,在早期版本中,您可以使用Iconv(我假设JRuby包括Iconv,实际上我对JRuby并不是很熟悉):

1.8.7 :001 > f
 => "\303\226\303\207\303\204\303\234360\303\223\303\257\303\222\303\264\303\226\303\272\303\200\303\255.txt" 
1.8.7 :005 > g = Iconv.conv('iso-8859-1', 'utf-8', f)
 => "\326\307\304\334360\323\357\322\364\326\372\300\355.txt" 
1.8.7 :006 > URI.escape f
 => "%C3%96%C3%87%C3%84%C3%9C360%C3%93%C3%AF%C3%92%C3%B4%C3%96%C3%BA%C3%80%C3%AD.txt" 
1.8.7 :007 > URI.escape g
 => "%D6%C7%C4%DC360%D3%EF%D2%F4%D6%FA%C0%ED.txt" 

请注意,通常您不能依赖服务器使用任何特定的编码方式。它应该使用 utf-8,但在这种情况下显然没有。


这非常有帮助,我不知道在编码后需要进行URI.escape。 - KnuturO

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接