Eval没有字符串插值的字符串

AKA如何使用正则表达式找到未转义的字符序列?

给定环境设置:

@secret = "OH NO!" $secret = "OH NO!" @@secret = "OH NO!" 

给定字符串从一个看起来像这样的文件读入:

 some_str = '"\"#{:NOT&&:very}\" bad. \u262E\n#@secret \\#$secret \\\\#@@secret"' 

我想将它评估为Ruby字符串,但没有插值。 因此,结果应该是:

 puts safe_eval(some_str) #=> "#{:NOT&&:very}" bad. ☮ #=> #@secret #$secret \#@@secret 

相比之下, eval -lyly解决方案产生

 puts eval(some_str) #=> "very" bad. ☮ #=> OH NO! #$secret \OH NO! 

起初我尝试过:

 def safe_eval(str) eval str.gsub(/#(?=[{@$])/,'\\#') end 

但这在上面的恶意中间案例中失败,产生:

 #=> "#{:NOT&&:very}" bad. ☮ #=> #@secret \OH NO! \#@@secret 

您可以通过正则表达式来确保在要转义的字符之前存在偶数个反斜杠:

 def safe_eval(str) eval str.gsub( /([^\\](?:\\\\)*)#(?=[{@$])/, '\1\#' ) end 

…说:

  • 找一个不是反斜杠的字符[^\\]
  • 然后是两个反斜杠(?:\\\\)
    • 重复零次或多次*
  • 后跟一个文字#字符
  • 并确保在此之后您可以看到{@$字符。
  • 并用它代替
    • 非反斜杠 – 可能跟随偶数个反斜杠
    • 然后是反斜杠然后#

如何不使用eval呢? 根据聊天中的这个评论,所有必要的是转义引号,换行符和unicode字符。 这是我的解决方案:

 ESCAPE_TABLE = { /\\n/ => "\n", /\\"/ => "\"", } def expand_escapes(str) str = str.dup ESCAPE_TABLE.each {|k, v| str.gsub!(k, v)} #Deal with Unicode str.gsub!(/\\u([0-9A-Z]{4})/) {|m| [m[2..5].hex].pack("U") } str end 

在字符串上调用时,结果是(在您的变量环境中):

 "\"\"\#{:NOT&&:very}\" bad. ☮\n\#@secret \\\#$secret \\\\\#@@secret\"" 

虽然我宁愿不必专门处理unicode,但这是没有eval的唯一方法。