在Ruby中逃脱和转换字符串的最佳方法是什么?

时间:2011-12-26 22:42:47

标签: ruby escaping

Ruby是否有任何内置方法可以转义 unescaping字符串?在过去,我使用正则表达式;但是,我发现Ruby可能一直在内部进行这样的转换。也许这个功能在某个地方暴露出来。

到目前为止,我已经提出了这些功能。他们工作,但他们似乎有点hacky:

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

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

有更好的方法吗?

7 个答案:

答案 0 :(得分:15)

有一堆逃避方法,其中一些:

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

所以,它真的取决于你需要解决的问题。但我会避免使用inspect来逃避。

更新 - 有一个转储,检查使用它,它看起来就像你需要的那样:

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

答案 1 :(得分:15)

Caleb函数是我能找到的字符串#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

答案 2 :(得分:12)

更新:我不再同意我自己的答案,但我不想删除它,因为我怀疑其他人可能会走错路,而且已经有很多讨论了这个答案和它的替代方案,所以我认为它仍然有助于对话,但请不要在实际代码中使用这个答案。

如果您不想使用eval,但愿意使用YAML模块,则可以使用它:

require 'yaml'

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

YAML优于eval的优势在于它可能更安全。 cane不允许eval的所有使用。我已经看到了使用$SAFEeval的建议,但目前无法通过JRuby使用。

对于它的价值,Python确实支持unescaping backslashes

答案 3 :(得分:11)

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

我真的不喜欢这样做。这更像是一种好奇心,而不是我在实践中所做的事情。

答案 4 :(得分:10)

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

答案 5 :(得分:7)

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 \" \\"

答案 6 :(得分:4)