Como codificar URL uma string no Ruby

135

Como faço para URI::encodeuma string como:

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

para obtê-lo em um formato como:

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

conforme RFC 1738?

Aqui está o que eu tentei:

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>'

Além disso:

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>'

Eu procurei na Internet e não encontrei uma maneira de fazer isso, embora eu esteja quase certo de que outro dia fiz isso sem nenhum problema.

HRÓÐÓLFR
fonte
1
Talvez seja útil se você estiver usando o Ruby 1.9: yehudakatz.com/2010/05/05/…
apneadiving

Respostas:

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
fonte
2
force_encoding('binary')pode ser uma opção mais auto-documentada.
mu é muito curto
63
Eles preteriram esse método, use * CGI.escape*. -> http://www.ruby-forum.com/topic/207489#903709 . Você também deve ser capaz de usar URI.www_form_encode* URI.www_form_encode_component*, mas eu nunca usei aqueles
J-Rou
2
Não há necessidade de require 'open-uri'aqui. Você quis dizer require 'uri'?
pje
1
@ J-Rou, CGI.escape pode escapar URL inteiro, não escapa seletivamente os parâmetros de consulta, por exemplo, se você passar 'a=&!@&b=&$^'para CGI.escape, ele escapará de tudo com separadores de consulta, &portanto, isso pode ser usado apenas para consultar valores. Eu sugiro usar addressablegem, é mais intelectual trabalhar com URLs.
Alexander.Iljushkin
Eu precisava acessar arquivos no servidor remoto. A codificação com CGI não funcionou, mas o URI.encode fez o trabalho muito bem.
Tashows 19/03/19
82

Atualmente, você deve usar ERB::Util.url_encodeou CGI.escape. A principal diferença entre eles é a manipulação de espaços:

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

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

CGI.escapesegue a especificação dos formulários CGI / HTML e fornece uma application/x-www-form-urlencodedstring, que requer que os espaços sejam escapados +, enquanto ERB::Util.url_encodesegue a RFC 3986 , que exige que eles sejam codificados como %20.

Consulte " Qual é a diferença entre URI.escape e CGI.escape? " Para obter mais discussões.

Jenner La Fave
fonte
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"

Retirado do comentário de @ J-Rou

Jared Beck
fonte
11

Você pode usar Addressable::URIgemas para isso:

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" 

Ele usa um formato mais moderno do que CGI.escape, por exemplo, codifica corretamente o espaço como %20e não como +sinal. Você pode ler mais em " O tipo application / x-www-form-urlencoded " na Wikipedia.

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" 
Alexey Shein
fonte
Também pode fazer assim: CGI.escape('Hello, this is me').gsub("+", "%20") => Hello%2C%20this%20is%20me"se não quiser usar gemas #
Raccoon
5

Criei uma gema para tornar o material de codificação de URI mais limpo para usar em seu código. Ele cuida da codificação binária para você.

Execute gem install uri-handlere use:

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"

Ele adiciona a funcionalidade de conversão de URI à classe String. Você também pode passar um argumento com a string de codificação opcional que você gostaria de usar. Por padrão, ele define a codificação 'binária' se a codificação direta UTF-8 falhar.

foomip
fonte
2

Código:

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

Resultado:

http://localhost/with%20spaces%20and%20spaces
Thiago Falcao
fonte
Se o servidor de recebimento for antigo, ele poderá não responder bem ao CGI.escape. Essa ainda é uma alternativa válida.
cesartalves
2

Inicialmente, eu estava tentando escapar de caracteres especiais apenas em um nome de arquivo, não no caminho, de uma string de URL completa.

ERB::Util.url_encode não funcionou para o meu uso:

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

Com base em duas respostas em " Por que o URI.escape () está marcado como obsoleto e onde é essa constante REGEXP :: UNSAFE? ", Parece que URI::RFC2396_Parser#escapeé melhor do que usar URI::Escape#escape. No entanto, ambos estão se comportando da mesma maneira para mim:

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
fonte
2

Se você deseja "codificar" um URL completo sem precisar pensar em dividi-lo manualmente em diferentes partes, achei o seguinte funcionando da mesma maneira que costumava usar URI.encode:

URI.parse(my_url).to_s
Glenn 'devalias'
fonte