Как URL кодировать строку в Ruby

135

Как мне 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>'

Я посмотрел все об Интернете и не нашел способа сделать это, хотя я почти уверен, что на днях я сделал это без каких-либо проблем.

HRÓÐÓLFR
источник
1
Может быть полезно, если вы используете Ruby 1.9: yehudakatz.com/2010/05/05/…
до

Ответы:

179
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"
Kain
источник
2
force_encoding('binary')может быть более самодокументированным выбором.
мю слишком коротка
63
Они осудили этот метод, CGI.escapeвместо этого используйте * *. -> http://www.ruby-forum.com/topic/207489#903709 . Вы также должны быть в состоянии использовать URI.www_form_encode* URI.www_form_encode_component*, но я никогда не использовал их
J-Rou
2
Нет необходимости require 'open-uri'здесь. Вы имели в виду require 'uri'?
2013 г.
1
@ J-Rou, CGI.escape может экранировать весь URL-адрес, он не экранирует выборочно параметры запроса, например, если вы передадите 'a=&!@&b=&$^'CGI.escape, он будет экранировать все с разделителями запросов, &поэтому его можно использовать только для запроса значений. Я предлагаю использовать addressablegem, это более интеллектуальная работа с URL.
Александр.
Мне нужно было получить доступ к файлам на удаленном сервере. Кодирование с помощью CGI не сработало, но URI.encode отлично сработал.
Ташоу
82

В настоящее время вы должны использовать 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? » Для дальнейшего обсуждения.

Дженнер Ла Фав
источник
70
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-Rou

Джаред Бек
источник
11

Вы можете использовать Addressable::URIгем для этого:

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-urlencoded " в Википедии.

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" 
Алексей Шеин
источник
Также можете сделать так: CGI.escape('Hello, this is me').gsub("+", "%20") => Hello%2C%20this%20is%20me"если не хотите использовать какие-либо драгоценные камни
Енот
5

Я создал гем, чтобы сделать кодировку 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 не удалась.

foomip
источник
2

Код:

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

Результат:

http://localhost/with%20spaces%20and%20spaces
Тьяго Фалькао
источник
Если принимающий сервер старый, он может плохо отвечать на CGI.escape. Это все еще действительная альтернатива.
cesartalves
2

Первоначально я пытался экранировать специальные символы только в имени файла, а не в пути, из полной строки 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"
kangkyu
источник
2

Если вы хотите «закодировать» полный URL-адрес, не думая о том, чтобы вручную разбить его на разные части, я обнаружил, что следующее работает так же, как и раньше URI.encode:

URI.parse(my_url).to_s
Гленн 'Девалиас'
источник