如何在 Ruby 中对字符串进行 URL 编码

如何使用字符串 URI::encode:

\x12\x34\x56\x78\x9a\xbc\xde\xf1\x23\x45\x67\x89\xab\xcd\xef\x12\x34\x56\x78\x9a

以这样的形式:

%124Vx%9A%BC%DE%F1%23Eg%89%AB%CD%EF%124Vx%9A

根据 RFC 1738?

我试过这么做:

irb(main):123:0> URI::encode "\x12\x34\x56\x78\x9a\xbc\xde\xf1\x23\x45\x67\x89\xab\xcd\xef\x12\x34\x56\x78\x9a"
ArgumentError: invalid byte sequence in UTF-8
from /usr/local/lib/ruby/1.9.1/uri/common.rb:219:in `gsub'
from /usr/local/lib/ruby/1.9.1/uri/common.rb:219:in `escape'
from /usr/local/lib/ruby/1.9.1/uri/common.rb:505:in `escape'
from (irb):123
from /usr/local/bin/irb:12:in `<main>'

另外:

irb(main):126:0> CGI::escape "\x12\x34\x56\x78\x9a\xbc\xde\xf1\x23\x45\x67\x89\xab\xcd\xef\x12\x34\x56\x78\x9a"
ArgumentError: invalid byte sequence in UTF-8
from /usr/local/lib/ruby/1.9.1/cgi/util.rb:7:in `gsub'
from /usr/local/lib/ruby/1.9.1/cgi/util.rb:7:in `escape'
from (irb):126
from /usr/local/bin/irb:12:in `<main>'

我在互联网上搜索了所有资料,还没有找到这样做的方法,尽管我几乎可以肯定的是,前几天我这样做的时候没有遇到任何麻烦。

196972 次浏览
str = "\x12\x34\x56\x78\x9a\xbc\xde\xf1\x23\x45\x67\x89\xab\xcd\xef\x12\x34\x56\x78\x9a".force_encoding('ASCII-8BIT')
puts CGI.escape str




=> "%124Vx%9A%BC%DE%F1%23Eg%89%AB%CD%EF%124Vx%9A"

我创建了一个 gem 来使 URI 编码更加清晰,以便在代码中使用。它为您处理二进制编码。

运行 gem install uri-handler,然后使用:

require 'uri-handler'


str = "\x12\x34\x56\x78\x9a\xbc\xde\xf1\x23\x45\x67\x89\xab\xcd\xef\x12\x34\x56\x78\x9a".to_uri
# => "%124Vx%9A%BC%DE%F1%23Eg%89%AB%CD%EF%124Vx%9A"

它将 URI 转换功能添加到 String 类中。您还可以使用希望使用的可选编码字符串将参数传递给它。默认情况下,如果直接 UTF-8编码失败,它将设置为编码“二进制”。

str = "\x12\x34\x56\x78\x9a\xbc\xde\xf1\x23\x45\x67\x89\xab\xcd\xef\x12\x34\x56\x78\x9a"
require 'cgi'
CGI.escape(str)
# => "%124Vx%9A%BC%DE%F1%23Eg%89%AB%CD%EF%124Vx%9A"

摘自@J-Ru 的评论

你可以使用 Addressable::URI gem:

require 'addressable/uri'
string = '\x12\x34\x56\x78\x9a\xbc\xde\xf1\x23\x45\x67\x89\xab\xcd\xef\x12\x34\x56\x78\x9a'
Addressable::URI.encode_component(string, Addressable::URI::CharacterClasses::QUERY)
# "%5Cx12%5Cx34%5Cx56%5Cx78%5Cx9a%5Cxbc%5Cxde%5Cxf1%5Cx23%5Cx45%5Cx67%5Cx89%5Cxab%5Cxcd%5Cxef%5Cx12%5Cx34%5Cx56%5Cx78%5Cx9a"

它使用比 CGI.escape更现代的格式,例如,它正确地将空间编码为 %20而不是 +符号,你可以在维基百科的“ Application/x-www-form-urlencode 类型”中阅读更多。

2.1.2 :008 > CGI.escape('Hello, this is me')
=> "Hello%2C+this+is+me"
2.1.2 :009 > Addressable::URI.encode_component('Hello, this is me', Addressable::URI::CharacterClasses::QUERY)
=> "Hello,%20this%20is%20me"

现在,你应该使用 ERB::Util.url_encode或者 CGI.escape。它们之间的主要区别在于它们对空间的处理:

>> ERB::Util.url_encode("foo/bar? baz&")
=> "foo%2Fbar%3F%20baz%26"


>> CGI.escape("foo/bar? baz&")
=> "foo%2Fbar%3F+baz%26"

CGI.escape遵循 CGI/HTML 表单规范并给出一个 application/x-www-form-urlencoded字符串,这需要将空格转义为 +,而 ERB::Util.url_encode遵循 RFC 3986,这需要将空格编码为 %20

有关更多讨论,请参见“ URI.escape 和 CGI escape 的区别是什么?”。

我最初只是试图从一个完整的 URL 字符串中转义文件名中的特殊字符,而不是路径中的特殊字符。

ERB::Util.url_encode不适合我的使用:

helper.send(:url_encode, "http://example.com/?a=\11\15")
# => "http%3A%2F%2Fexample.com%2F%3Fa%3D%09%0D"

根据“ 为什么 URI.escape ()被标记为过时,并且这个 REGEXP: : UNSAFE 常量在哪里?”中的两个答案,看起来 URI::RFC2396_Parser#escape比使用 URI::Escape#escape要好。然而,在我看来,它们的表现是一样的:

URI.escape("http://example.com/?a=\11\15")
# => "http://example.com/?a=%09%0D"
URI::Parser.new.escape("http://example.com/?a=\11\15")
# => "http://example.com/?a=%09%0D"

如果你想“编码”一个完整的 URL,而不必考虑手动将其分成不同的部分,我发现以下工作方式与我过去使用 URI.encode的方式相同:

URI.parse(my_url).to_s

密码:

str = "http://localhost/with spaces and spaces"
encoded = URI::encode(str)
puts encoded

结果:

http://localhost/with%20spaces%20and%20spaces