Лучший способ избежать и unescape строк в Ruby?
Есть ли у Ruby встроенный метод для экранирования и отмены строк? Раньше я использовал регулярные выражения; однако, мне кажется, что Ruby, вероятно, делает такие преобразования внутри себя все время. Возможно, эта функция отображается где-то.
До сих пор я придумывал эти функции. Они работают, но они кажутся немного взломанными:
def escape(s)
s.inspect[1..-2]
end
def unescape(s)
eval %Q{"#{s}"}
end
Есть ли лучший способ?
Ответы
Ответ 1
Ruby 2.5 добавил String#undump
как дополнение к String#dump
:
$ irb
irb(main):001:0> dumped_newline = "\n".dump
=> "\"\\n\""
irb(main):002:0> undumped_newline = dumped_newline.undump
=> "\n"
С этим:
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 \" \\"
Ответ 2
Функция Caleb была ближайшей вещью к обратному тексту String #inspect, которую я смог найти, однако он содержал две ошибки:
- \\не обрабатывалось правильно.
- \x.. сохранил обратную косую черту.
Я исправил вышеуказанные ошибки, и это обновленная версия:
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
Ответ 3
Есть несколько методов ускорения, некоторые из них:
# Regexp escapings
>> Regexp.escape('\*?{}.')
=> \\\*\?\{\}\.
>> URI.escape("test=100%")
=> "test=100%25"
>> CGI.escape("test=100%")
=> "test%3D100%25"
Итак, это действительно зависит от проблемы, которую вам нужно решить. Но я бы избегал использования проверки для экранирования.
Обновление - есть дамп, проверяет его использование, и похоже, что это то, что вам нужно:
>> "\n\t".dump
=> "\"\\n\\t\""
Ответ 4
Обновление: я больше не согласен с моим собственным ответом, но я предпочел бы не удалять его, так как подозреваю, что другие могут пойти по этому неправильному пути, и уже было много обсуждений этого ответа и его альтернатив, поэтому я думаю, это все еще способствует разговору, но, пожалуйста, не используйте этот ответ в реальном коде.
Если вы не хотите использовать eval
, но готовы использовать модуль YAML
, вы можете использовать его вместо:
require 'yaml'
def unescape(s)
YAML.load(%Q(---\n"#{s}"\n))
end
Преимущество YAML
перед eval
состоит в том, что он, по-видимому, безопаснее. cane
запрещает любое использование eval
. Я видел рекомендации по использованию $SAFE
вместе с eval
, но в данный момент он недоступен через JRuby.
Что бы это ни стоило, в Python есть встроенная поддержка обратной косой черты.
Ответ 5
Ruby inspect
может помочь:
"a\nb".inspect
=> "\"a\\nb\""
Обычно, если мы печатаем строку со встроенной строкой, мы получаем:
puts "a\nb"
a
b
Если мы напечатаем проверенную версию:
puts "a\nb".inspect
"a\nb"
Назначьте проверенную версию переменной, и у вас будет экранированная версия строки.
Чтобы отменить экранирование, eval
строка:
puts eval("a\nb".inspect)
a
b
Мне не нравится это делать. Это скорее любопытство, чем то, что я сделал бы на практике.
Ответ 6
YAML ::unescape
, похоже, не пропускает символы кавычек, например. '
и "
. Я предполагаю, что это по дизайну, но мне грустно.
Вы определенно не хотите использовать eval
для произвольных или предоставленных клиентом данных.
Это то, что я использую. Обрабатывает все, что я видел, и не вносит никаких зависимостей.
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
Ответ 7
Я подозреваю, что Shellwords.escape
сделает то, что вы ищете
https://ruby-doc.org/stdlib-1.9.3/libdoc/shellwords/rdoc/Shellwords.html#method-c-shellescape