Подтвердить что ты не робот

Лучший способ избежать и unescape строк в Ruby?

Есть ли у Ruby встроенный метод для экранирования и отмены строк? Раньше я использовал регулярные выражения; однако, мне кажется, что Ruby, вероятно, делает такие преобразования внутри себя все время. Возможно, эта функция отображается где-то.

До сих пор я придумывал эти функции. Они работают, но они кажутся немного взломанными:

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

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

Есть ли лучший способ?

4b9b3361

Ответ 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