我正在试图弄清楚如何更好地解析具有如下值的文本行:
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更好,更准确的正则表达式
答案 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+)/)
如果输入文本的引号以空格开头,则会中断。