Ruby a-t-il une méthode intégrée pour échapper et les chaînes sans échappement? Dans le passé, j'ai utilisé des expressions régulières; cependant, il me semble que Ruby effectue probablement de telles conversions en interne tout le temps. Peut-être que cette fonctionnalité est exposée quelque part.
Jusqu'à présent, je suis venu avec ces fonctions. Ils fonctionnent, mais ils semblent un peu hacky:
def escape(s)
s.inspect[1..-2]
end
def unescape(s)
eval %Q{"#{s}"}
end
Y a-t-il une meilleure façon?
Ruby 2.5 ajouté String#undump
en complément de String#dump
:
$ irb
irb(main):001:0> dumped_newline = "\n".dump
=> "\"\\n\""
irb(main):002:0> undumped_newline = dumped_newline.undump
=> "\n"
Avec ça:
def escape(s)
s.dump[1..-2]
end
def unescape(s)
"\"#{s}\"".undump
end
$irb
irb(main):001:0> escape("\n \" \\")
=> "\\n \\\" \\\\"
irb(main):002:0> unescape("\\n \\\" \\\\")
=> "\n \" \\"
Il existe un tas de méthodes d'échappement, certaines d'entre elles:
# Regexp escapings
>> Regexp.escape('\*?{}.')
=> \\\*\?\{\}\.
>> URI.escape("test=100%")
=> "test=100%25"
>> CGI.escape("test=100%")
=> "test%3D100%25"
Donc, cela dépend vraiment du problème que vous devez résoudre. Mais j'éviterais d'utiliser inspect pour s'échapper.
Mise à jour - il y a un vidage, inspectez les utilisations et il semble que c'est ce dont vous avez besoin:
>> "\n\t".dump
=> "\"\\n\\t\""
La fonction Caleb était la chose la plus proche de l'inverse de String #inspect que j'ai pu trouver, mais elle contenait deux bogues:
J'ai corrigé les bugs ci-dessus et voici la version mise à jour:
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
Mise à jour : je ne suis plus d'accord avec ma propre réponse, mais je préférerais ne pas la supprimer car je soupçonne que d'autres peuvent emprunter cette mauvaise voie, et il y a déjà eu beaucoup de discussions sur cette réponse et ses alternatives, donc je pense que cela contribue toujours à la conversation, mais veuillez ne pas utiliser cette réponse dans le vrai code.
Si vous ne souhaitez pas utiliser eval
, mais souhaitez utiliser le module YAML
, vous pouvez l'utiliser à la place:
require 'yaml'
def unescape(s)
YAML.load(%Q(---\n"#{s}"\n))
end
L'avantage de YAML
sur eval
est qu'il est probablement plus sûr. cane
interdit toute utilisation de eval
. J'ai vu des recommandations pour utiliser $SAFE
avec eval
, mais qui n'est pas disponible via JRuby actuellement.
Pour ce que cela vaut, Python a un support natif pour barres obliques inverses .
Ruby inspect
peut vous aider:
"a\nb".inspect
=> "\"a\\nb\""
Normalement, si nous imprimons une chaîne avec un saut de ligne intégré, nous obtenons:
puts "a\nb"
a
b
Si nous imprimons la version inspectée:
puts "a\nb".inspect
"a\nb"
Assignez la version inspectée à une variable et vous aurez la version d'échappement de la chaîne.
Pour annuler l'échappement, eval
la chaîne:
puts eval("a\nb".inspect)
a
b
Je n'aime pas vraiment le faire de cette façon. C'est plus une curiosité que quelque chose que je ferais en pratique.
YAML's ::unescape
ne semble pas échapper aux guillemets, par exemple '
et "
. Je suppose que c'est par conception, mais cela me rend triste.
Vous ne voulez certainement pas utiliser eval
sur des données arbitraires ou fournies par le client.
C'est ce que j'utilise. Gère tout ce que j'ai vu et n'introduit aucune dépendance.
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
Je soupçonne que Shellwords.escape
fera ce que vous cherchez
https://Ruby-doc.org/stdlib-1.9.3/libdoc/shellwords/rdoc/Shellwords.html#method-c-shellescape