在Ruby中使用正则表达式解析引号内的单个单词和单词组

时间:2010-10-13 10:11:46

标签: ruby regex parsing text

我正在试图弄清楚如何更好地解析具有如下值的文本行:

line1  
'Line two' fudgy whale 'rolly polly'  
fudgy 'line three' whale  
fudgy whale 'line four'  
'line five' 'fish heads' 
line six  

我希望使用单个正则表达式来显示所需的输出。我已经知道如何克服它以获得所需的输出但我想要一个表达式

期望的输出

["line1"]
["Line two", "fudgy", "whale", "rolly polly"]
["fudgy", "line three", "whale"]
["fudgy", "whale", "line four"]
["line five", "fish heads"]
["line", "six"]

线路读数已经通过Cucumber为我处理了。每一行都被读作一个字符串值,我想解析单个单词和单引号中包含的任意数量的单词。我对正则表达式知之甚少,但是我使用正则表达式“或”运算符(“|”)将正则表达式混为一谈。

采用该正则表达式我首先尝试使用字符串拆分解析每一行:

text_line.split(/(\w+)|'(.*?)'/)

这导致了以下,不太可接受的数组:

["", "line1"]
["", "Line two", " ", "fudgy", " ", "whale", " ", "rolly polly"]
["", "fudgy", " ", "line three", " ", "whale"]
["", "fudgy", " ", "whale", " ", "line four"]
["", "line five", " ", "fish heads"]
["", "line", "", "six"]

我接下来尝试使用扫描而不是拆分,我看到了这一点:

text_line.scan(/(\w+)|'(.*?)'/)
[["line1", nil]]
[[nil, "Line two"], ["fudgy", nil], ["whale", nil], [nil, "rolly polly"]]
[["fudgy", nil], [nil, "line three"], ["whale", nil]]
[["fudgy", nil,], ["whale", nil], [nil, "line four"]]
[[nil, "line five"], [nil, "fish heads"]]
[["line", nil], [nil, "six",]]

所以我可以看到正则表达式“或”运算符为每个可能的“或”位置生成一个有意义的值。知道我发现我可以使用scan,flatten和compact来清理它,给我所需的输出

text_line.scan(/(\w+)|'(.*?)'/).flatten.compact
["line1"]
["Line two", "fudgy", "whale", "rolly polly"]
["fudgy", "line three", "whale"]
["fudgy", "whale", "line four"]
["line five", "fish heads"]
["line", "six"]

但是使用扫描,展平和紧凑看起来非常难看,似乎我只是在修补自己糟糕的正则表达式。我正在考虑用火腿手工修复我构造得不好的正则表达式的草率输出,我应该写一个更好的正则表达式。

那么,是否可以使用单个正则表达式来解析上面的行并获得所需的输出?我可能会开始使用正则表达式,但我想我是否能够以某种方式对这个或那些进行分组,这样他们每个组只能返回一个可能是我正在寻找的值。

请随意建议替代解决方案,但我正在寻找以Ruby方式完成的优雅解决方案,因为我正在尝试自学如何使用该语言。

提前感谢您的时间。

编辑以整合tininfi更好,更准确的正则表达式

3 个答案:

答案 0 :(得分:1)

如果要获取不同大小的数组,可以分两步执行:.split.scan。 在你的情况下,.scan()的两边都有|,这就是为什么你遇到nil的问题(这应该是有用的,但不是你的情况)。因此,您可以使用.flatten.compact或添加.delete的第3步。

text.split("\n").map{|i|p i.scan(/'([^']+)'|(\w+)/).flatten.compact}
text.split("\n").map{|i|p i.scan(/'[^']+'|\w+/).map{|i|i.delete "'"}}

答案 1 :(得分:0)

您可以将正则表达式简化为:

'(.*?)'|(\w+)

你仍然需要使用扁平和紧凑,但至少它看起来更好一点。并不是你指定了需要,但这将允许字符串:

'quote one' 'quote two'

以下被拒绝,因为它不如原始解决方案那么优雅。
您可以尝试:

regex = %r((\w+)|(?:')([^"\r\n]*)(?:'))
text.split(regex).delete_if { |x| x.strip.empty? }

<击>

答案 2 :(得分:0)

我有一种感觉,你仍然不喜欢这样,但这是最接近“单一的正则表达式”我可以提出的:

text_line.scan(/(?<=')(?:[^\s][^']*)(?=')|(?:\w+)/)

如果输入文本的引号以空格开头,则会中断。