关于 ruby??:eval 文件中的某些正则表达式以替换字符串中的字符

eval certain regex from file to replace chars in string

我是 ruby?? 新手,所以请原谅我的无知 :)

我刚刚了解了 eval 并且读到了它的阴暗面。

到目前为止我读过的内容:

  • Ruby 中的 eval 什么时候是合理的?

  • \\'eval\\' 应该是讨厌的吗?

  • Ruby Eval 和 Ruby 代码的执行

所以我要做的是读取一个文件,其中有一些文本,例如 /e/ 3 它将在评估后将每个 e 替换为 3

所以到目前为止我做了什么:(工作但是..)

1
2
3
4
5
6
7
8
9
def evaluate_lines
  result="elt"
  IO.foreach("test.txt") do |reg|
    reg=reg.chomp.delete(' ')
    puts reg
    result=result.gsub(eval(reg[0..2]),"#{reg[3..reg.length]}" )
    p result
  end
end

test.txt 文件的内容

1
2
3
4
5
/e/ 3
/l/ 1
/t/ 7
/$/ !
/$/ !!

这只是因为我知道文件中行的长度。

所以假设我的文件具有以下 /a-z/ 3 我的程序将无法执行预期的操作。

笔记

我尝试使用 Regexp.new reg 并导致以下 /\\/e\\/3/ 在这种情况下不是很有帮助。

`Regexp

的简单示例

1
2
3
4
str="/e/3"
result="elt"
result=result.gsub(Regexp.new str)
p result #outputs: #<Enumerator:"elt":gsub(/\\/e\\/3/)>

我已经尝试去除斜线,但即使这不会提供所需的结果,因此 gsub() 需要两个参数,例如这个 gsub(/e/,"3").

关于 Regexp 的用法,我已经阅读 Convert a string to regular expression ruby??


虽然您可以编写一些东西来解析该文件,但它很快就会变得复杂,因为您必须解析正则表达式。考虑 /\\/foo\\\\/.

有许多不完整的解决方案。您可以在空格上拆分,但这会在 /foo bar/.

上失败

1
re, replace = line.split(/\\s+/, 2)

您可以使用正则表达式。这是第一次刺伤。

1
match ="/3/ 4".match(%r{^/(.*)/\\s+(.+)})

这在转义 / 上失败,我们需要更复杂的东西。

1
match = '/3\\// 4'.match(%r{\\A / ((?:[^/]|\\\\/)*) / \\s+ (.+)}x)

我猜你老师的意图不是让你解析正则表达式。出于分配的目的,在空格上拆分可能没问题。你应该和你的老师澄清一下。

这是一种糟糕的数据格式。它是非标准的,难以解析,并且在替换上有限制。即使是制表符分隔的文件也会更好。

现在几乎没有理由使用非标准格式。最简单的事情是为文件使用标准数据格式。 YAML 或 JSON 是最明显的选择。对于这样简单的数据,我建议使用 JSON.

1
2
3
4
[
  {"re":"e","replace":"3" },
  {"re":"l","replace":"1" }
]

解析文件很简单,使用内置的 JSON 库。

1
2
require 'json'
specs = JSON.load("test.json")

然后您可以将它们用作哈希列表。

1
2
3
4
5
6
7
specs.each do |spec|
  # No eval necessary.
  re = Regexp.new(spec["re"])

  # `gsub!` replaces in place
  result.gsub!(re, spec["replace"])
end

数据文件是可扩展的。例如,如果稍后您想添加正则表达式选项。

1
2
3
4
[
  {"re":"e","replace":"3" },
  {"re":"l","replace":"1","options": ['IGNORECASE'] }
]

虽然老师可能指定了一个糟糕的格式,但对糟糕的要求提出回避是作为开发人员的好习惯。


这是一个非常简单的例子,它使用 s/.../.../s/.../.../g 之类的 vi 表示法:

1
2
3
4
5
6
7
8
9
10
11
12
def rsub(text, spec)
  _, mode, repl, with, flags = spec.match(%r[\\A(.)\\/((?:[^/]|\\\\/)*)/((?:[^/]|\\\\/)*)/(\\w*)\\z]).to_a

  case (mode)
  when 's'
    if (flags.include?('g'))
      text.gsub(Regexp.new(repl), with)
    else
      text.sub(Regexp.new(repl), with)
    end
  end
end

请注意,匹配器会查找非斜线字符 ([^/]) 或文字-斜线组合 (\\\\/) 并相应地拆分这两部分。

你可以在哪里得到这样的结果:

1
2
3
4
5
6
7
8
9
10
11
rsub('sandwich', 's/and/or/')
# =>"sorwich"

rsub('and/or', 's/\\//,/')
# =>"and,or"

rsub('stack overflow', 's/o/O/')
# =>"stack Overflow"

rsub('stack overflow', 's/o/O/g')
# =>"stack OverflOw"

这里的原则是您可以使用一个非常简单的正则表达式来解析您输入的正则表达式并将清理后的数据输入到 Regexp.new 中。这里绝对不需要 eval ,如果有任何严重限制你可以做的事情。

通过一些工作,您可以更改该正则表达式以解析现有文件中的内容并使其执行您想要的操作。