改进ruby代码上的正则表达式

时间:2014-07-29 14:40:27

标签: ruby regex

我有以下脚本

file = File.new("jobs.txt", 'r')
h = {}
jobs = []
salaries= []
while (line = file.gets)
    if ( line =~ /CODE/)
        cargos << line.gsub("\n", "")
    elsif (line =~ /SALARY/)
            salarios << line.gsub("\n", "")
    end
end
h = Hash[jobs.zip(salaries)]
h.each { |code, salary| puts "#{code} ------ #{salary}" }

它完成了工作但是我想让正则表达式忽略/ CODE /它匹配并返回行的其余部分,是否可以在正则表达式上执行此操作或者我必须自己编写代码(替换字符串)或类似的东西)。

我主要想弄清楚如何使代码尽可能小。

3 个答案:

答案 0 :(得分:3)

您的代码不是非常惯用的。这是未经测试的,但看起来是正确的:

salarios = []
cargos = []
File.foreach("jobs.txt") do |line|

  if ( line =~ /CODE/)
    cargos << line[/CODE - (\S+)/, 1]
  elsif (line =~ /SALARY/)
    salarios << line[/SALARY - (\S+)/, 1]
  end

end
h = Hash[cargos.zip(salaries)]
h.each { |code, salary| puts "#{code} ------ #{salary}" }

line[/CODE = (\S+)/, 1]利用String的[]方法,它允许我们传递许多不同类型的参数。在这种情况下,我正在使用带有捕获的正则表达式模式。 1告诉Ruby返回模式中的第一个捕获:

'CODE - XXXXX'[/CODE - (\S+)/, 1] # => "XXXXX"

\S+表示“一个或多个非空格字符”,所以基本上模式是“查找'代码 - 然后捕获下一个字符串直到空格,制表符,换行符或回车符找到了。

查找和捕获值的另一种方法是在模式匹配并包含捕获时利用Ruby的“魔术”变量设置:

if ( line =~ /CODE - (\S+)/)
  cargos << $1
elsif (line =~ /SALARY - (\S+)/)
  salarios << $1
end

以下是一些证据:

'CODE - XXXXX' =~ /CODE - (\S+)/
$1 # => "XXXXX"

有些人不喜欢使用Regexp魔术变量;只要你立即使用它们,在其他任何东西都有机会运行另一个正则表达式匹配之前,你就可以了。如果发生另一场比赛,则可以覆盖变量并且您将遇到错误。

返回您的代码。使用带有块的foreach来读取文件中的行,而不是打开并分配给变量。在块退出后,Ruby将自动关闭文件。

答案 1 :(得分:1)

如果输入类似于此CODE - XXXXX SALARY - XXXX OTHER INFO - XXXX,则产生代码:

cargos << $1 if (line =~ /CODE\D+(\d+)/)
salarios << $1 if (line =~ /SALARY\D+(\d+)/)

此处正则表达式与CODE匹配,后跟至少一个非数字(\D+),后跟捕获的数字,这些数字代表代码

希望它有所帮助。

答案 2 :(得分:1)

分别搜索"CODE""SALARY"时需要注意。如果数据存在问题,您可能永远不会知道它(例如,"...CODE...CODE....SALARY..."或可能引发异常(例如,zip和{{1时执行jobs是不同的大小)。

这是我怎么做的。如果数据正常,则该方法返回所需的结果,否则返回salaries

<强>代码

nil

<强>实施例

def doit(lines)
  a = lines.select { |s| s =~ /CODE|SALARY/ }
  return nil unless a.size.even?
  jobs, salaries = a.each_slice(2).to_a.transpose
  return nil unless jobs.all?     { |l| l.scan(/CODE|SALARY/) == ["CODE"]   }
  return nil unless salaries.all? { |l| l.scan(/CODE|SALARY/) == ["SALARY"] }
  jobs.zip salaries
end

<强>解释

  • text =<<-_ CODE and SALARY are good, but CODE without any SALARY is not so good _ doit(text.split("\n")) #=> [["CODE and", "SALARY are good, but"], ["CODE without", "SALARY is not"]] text =<<-_ CODE and SALARY are good, but SALARY without CODE is even better _ doit(text.split("\n")) #=> nil text =<<-_ CODE and SALARY are good, but CODE is the main thing _ doit(text.split("\n")) #=> nil 会删除包含lines.select { |s| s =~ /CODE|SALARY/ }"CODE"一词的所有行。
  • "SELECT"将所选行对,然后将枚举数转换为具有两列
  • 的数组
  • each_slice(2).to_a提取数组的列。第一列应该是transpose行;第二列,"CODE"行。
  • "SALARY"确保每个jobs.all? { |l| l.scan(/CODE|SALARY/) == ["CODE"] }行只包含"CODE"一次,不包含"CODE"。下一行代码与"SALARY"行相反。
  • 最后一行返回所需的结果。