【问题标题】:eval certain regex from file to replace chars in string从文件中评估某些正则表达式以替换字符串中的字符
【发布时间】:2020-08-26 09:11:02
【问题描述】:

我是 ruby​​ 新手,请原谅我的无知 :)

我刚刚了解了eval,并了解了它的阴暗面。

到目前为止我读到的:

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

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

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 文件的内容

/e/ 3
/l/ 1
/t/ 7
/$/ !
/$/ !!

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

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

注意

我尝试使用Regexp.new reg,结果出现以下/\/e\/3/,这在这种情况下不是很有帮助。

`Regexp 的简单示例

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

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

关于正则表达式的用法,我已经看过Convert a string to regular expression ruby

【问题讨论】:

  • 当你有Regex.new时,你不需要eval。我也远离命名您的输入文件.rb,因为它肯定不是 Ruby 代码。要使其与Regexp.new 一起使用,请先去掉前导斜杠和尾随斜杠
  • @tadman,对不起,我输入的是 rb 而不是 txt 抱歉。我编辑了帖子。无论如何,使用 regexp.new 没有帮助,因此我会给我不想要的结果,因为方法 gsub 需要两个参数,例如 gsub(/e/, "3")
  • 您可以控制输入文件的格式吗?如果是这样,你可以让你的生活轻松一百倍。
  • 好吧,虽然我可以完全控制这个文件,但格式是由分配者提供的。并且this 不在桌面上,因此 test.txt 在测试时可能会更大:)。即使我可以去掉反斜杠,我也必须将第二个参数提供给 gsub 方法

标签: regex ruby string


【解决方案1】:

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

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

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

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

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

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

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

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


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

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

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

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

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

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

specs.each do |spec|
  # No eval necessary.
  re = Regexp.new(spec["re"])

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

数据文件是可扩展的。例如,如果稍后您想添加regex options

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

虽然老师可能指定了一个糟糕的格式,但对于作为开发人员来说,推回糟糕的要求是一种很好的做法。

【讨论】:

  • 感谢您努力帮助人类。关于您的代码的小闲聊:作为一名学生和 ruby​​ 初学者,我还没有使用过 JSON 库。但看看你的代码,我有点明白你答案的 JSON 部分发生了什么。所以,让我看看我是否明白这一点: 1.你会重写 test.txt 文件中的所有修改并将其放入不同格式的 JSON 文件中吗? 2.您会从txt中获取数据并将其放入哈希然后遍历它吗?那么那些[ { "re": "e", "replace": "3" }, { "re": "l", "replace": "1" } ] 写在哪里?
  • 无论如何,我会再次询问教授以更好地了解他的意图,然后我可以看看这个答案是否是答案,但现在我非常感谢您的帮助和 +1跨度>
  • @studentaccount4 是的,如果这是生产,我会将文件更改为 JSON。你可以像我一样手动翻译。或者,既然数据这么简单,就用正则表达式解析原始文件,放入合理的数据结构,用JSON.dump写成JSON。由于这是一项作业,是的,请询问您的教授他们希望您做什么。
【解决方案2】:

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

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

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

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

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,如果有任何严重限制你可以做的事情。

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

【讨论】:

  • 好吧,这看起来很酷,我想我明白了你在这里想要完成的事情,但我有一个关于_,使用的小问题@这是干什么用的?!另外,盯着你写的代码我有一个小问题,请原谅我的无知,spec.match 的返回值是如何在模式、repl 和 with 之间分布的?!请你这么好心,在rsub()的第二行写一个关于发生了什么的详细解释,否则这应该会有所帮助:)
  • 在 Ruby 中 _ 是一个“无关紧要”的变量。第一个元素无关紧要,它是所有匹配项的集合。至于解释,对你来说不能太容易,那里有很多事情要做,但如果你把它分解并在irb 中试验那一行,它会更有意义。
猜你喜欢
  • 1970-01-01
  • 2021-10-10
  • 1970-01-01
  • 1970-01-01
  • 2012-04-26
  • 2017-05-19
  • 1970-01-01
  • 2017-07-16
  • 1970-01-01
相关资源
最近更新 更多