2011-12-26 9 views
23

¿Ruby tiene algún método incorporado para escaparse de y desenganchando cadenas? En el pasado, he usado expresiones regulares; sin embargo, se me ocurre que Ruby probablemente realice tales conversiones internamente todo el tiempo. Quizás esta funcionalidad esté expuesta en alguna parte.¿La mejor manera de escaparse y quitar cuerdas en Ruby?

Hasta ahora he llegado a estas funciones. Funcionan, pero parecen un poco hacky:

def escape(s) 
    s.inspect[1..-2] 
end 

def unescape(s) 
    eval %Q{"#{s}"} 
end 

¿Hay una manera mejor?

+2

escape con qué propósito? Para usar en la fuente de Ruby? –

+0

@mu es demasiado complicado: sí, escapa según las reglas de fuente de Ruby. – jwfearn

Respuesta

11

Si no desea utilizar eval, pero está dispuesto a utilizar el módulo YAML , se puede utilizar en su lugar:

require 'yaml' 

def unescape(s) 
    YAML.load(%Q(---\n"#{s}"\n)) 
end 

La ventaja de YAML sobre eval es que es presumiblemente más seguro. cane no permite el uso de eval. He visto recomendaciones para usar $SAFE junto con eval, pero eso no está disponible a través de JRuby actualmente.

Por lo que vale, Python tiene soporte nativo para unescaping backslashes.

+3

Gracias. Tomé su idea y la apliqué a JSON, 'JSON.parse (" [# {s}] "). Primero' – akuhn

+0

Falla para' \ n \ n' –

+0

parece que el código YAML y el código EVAL son diferentes. Por ejemplo s = "\\ xD8 \\ x96a" YAML.load (% Q (--- \ n "# {s}" \ n)) (eval% Q {"# {s}"}) devuelve valores diferentes – MKo

9

de Ruby inspect pueden ayudar:

"a\nb".inspect 
=> "\"a\\nb\"" 

Normalmente si imprimimos una cadena con una línea de alimentación integrada, que tendríamos:

puts "a\nb" 
a 
b 

Si es la impresión de la versión inspeccionado:

puts "a\nb".inspect 
"a\nb" 

Asigne la versión inspeccionada a una variable y tendrá la versión de escape de th e cuerda

para deshacer el escape, eval la cadena:

puts eval("a\nb".inspect) 
a 
b 

que no me gusta hacerlo de esta manera. Es más una curiosidad que algo que haría en la práctica.

+3

¡Peligro Robinson, Peligro! Usar eval para unescape de la cadena es realmente peligroso si la cadena es ingresada por el usuario. Permitiría al usuario ejecutar prácticamente cualquier cosa. –

+0

Sí, si es una entrada del usuario, debe desinfectarse primero. Pero no puede ejecutar nada, solo lo que podría ejecutar el ID de usuario que ejecuta el código, lo cual, en una aplicación correctamente escrita, se reducirán los privilegios o en un entorno limitado chroot. –

+0

Tienes razón. Pero en realidad, gran parte del valor de una caja no son los archivos del sistema operativo, sino sus datos. Arrastra la aplicación de raíles todo lo que quiera, pero aún necesita acceso a su base de datos. Entonces, si bien su atacante no puede hacer "todo", puede hacer mucho, incluso eliminar todos sus datos. –

14

Hay un montón de métodos que escapan, algunos de ellos:

# Regexp escapings 
>> Regexp.escape('\*?{}.') 
=> \\\*\?\{\}\. 
>> URI.escape("test=100%") 
=> "test=100%25" 
>> CGI.escape("test=100%") 
=> "test%3D100%25" 

Por lo tanto, es realmente depende del problema que necesita resolver. Pero evitaría usar inspeccionar para escapar.

Actualización - hay un basurero, inspeccionar utiliza eso, y parece que es lo que necesita:

>> "\n\t".dump 
=> "\"\\n\\t\"" 
+5

Me gustaría evitar 'inspeccionar' también. Esperaba que el propio código de fuga de Ruby estuviera disponible. Algo parecido a 'Ruby.escape (" \ t ") =>" \\ t "' y 'Ruby.unescape (" \\ t ") =>" \ t "' – jwfearn

10

El ::unescape de YAML no parece escapar a los caracteres de comillas, p. ' y ". Supongo que esto es por diseño, pero me entristece.

Definitivamente no desea utilizar eval en datos arbitrarios o suministrados por el cliente.

Esto es lo que uso.Maneja todo lo que he visto y no introduce ninguna dependencia.

UNESCAPES = { 
    'a' => "\x07", 'b' => "\x08", 't' => "\x09", 
    'n' => "\x0a", 'v' => "\x0b", 'f' => "\x0c", 
    'r' => "\x0d", 'e' => "\x1b", "\\\\" => "\x5c", 
    "\"" => "\x22", "'" => "\x27" 
} 

def unescape(str) 
    # Escape all the things 
    str.gsub(/\\(?:([#{UNESCAPES.keys.join}])|u([\da-fA-F]{4}))|\\0?x([\da-fA-F]{2})/) { 
    if $1 
     if $1 == '\\' then '\\' else UNESCAPES[$1] end 
    elsif $2 # escape \u0000 unicode 
     ["#$2".hex].pack('U*') 
    elsif $3 # escape \0xff or \xff 
     [$3].pack('H2') 
    end 
    } 
end 
14

función de Caleb era lo más parecido a la inversa de la cadena #inspect yo era capaz de encontrar, sin embargo, contenía dos errores:

  • \\ no se maneja correctamente.
  • \ x .. retuvo la barra diagonal inversa.

He arreglado los errores anteriores y esta es la versión actualizada:

UNESCAPES = { 
    'a' => "\x07", 'b' => "\x08", 't' => "\x09", 
    'n' => "\x0a", 'v' => "\x0b", 'f' => "\x0c", 
    'r' => "\x0d", 'e' => "\x1b", "\\\\" => "\x5c", 
    "\"" => "\x22", "'" => "\x27" 
} 

def unescape(str) 
    # Escape all the things 
    str.gsub(/\\(?:([#{UNESCAPES.keys.join}])|u([\da-fA-F]{4}))|\\0?x([\da-fA-F]{2})/) { 
    if $1 
     if $1 == '\\' then '\\' else UNESCAPES[$1] end 
    elsif $2 # escape \u0000 unicode 
     ["#$2".hex].pack('U*') 
    elsif $3 # escape \0xff or \xff 
     [$3].pack('H2') 
    end 
    } 
end 

# To test it 
while true 
    line = STDIN.gets 
    puts unescape(line) 
end 
+0

¡Gracias por las actualizaciones! Sin embargo, lo habría corregido si lo hubieras comentado. –

+0

@antirez esto es muy útil. Lo he incorporado en un módulo de marionetas que hice como [función de marionetas] (https://github.com/gene1wood/puppet-credstash/blob/4f5879192ab07bd8de07daeb49ab50e9d00ff563/lib/puppet/parser/functions/unescape.rb) –

+0

@ antirez Esta es la mejor respuesta que encontré hasta ahora. Solo un consejo, en lugar de usar hexadecimal, se pueden usar los caracteres de escape reales. Por ejemplo, en lugar de '" \ x0a "', puede ser '" \ n "'. Creo que esto es más claro. – rigon

Cuestiones relacionadas