Ruby扫描正则表达式

时间:2013-03-30 13:48:26

标签: ruby regex

我正在尝试拆分字符串:

"[test| blah] \n [foo |bar bar bar]\n[test| abc |123 | 456 789]"

进入以下数组:

[
  ["test","blah"]
  ["foo","bar bar bar"]
  ["test","abc","123","456 789"]
]

我尝试了以下内容,但这不太对:

"[test| blah] \n [foo |bar bar bar]\n[test| abc |123 | 456 789]"
.scan(/\[(.*?)\s*\|\s*(.*?)\]/)
# =>
# [
#   ["test", "blah"]
#   ["foo", "bar bar bar"]
#   ["test", "abc |123 | 456 789"]
# ]

我需要在每个管道而不是第一个管道上拆分。实现这一目标的正确正则表达式是什么?

4 个答案:

答案 0 :(得分:7)

 s = "[test| blah] \n [foo |bar bar bar]\n[test| abc |123 | 456 789]"
 arr = s.scan(/\[(.*?)\]/).map {|m| m[0].split(/ *\| */)}

答案 1 :(得分:6)

两种选择:

s = "[test| blah] \n [foo |bar bar bar]\n[test| abc |123 | 456 789]"

s.split(/\s*\n\s*/).map{ |p| p.scan(/[^|\[\]]+/).map(&:strip) }
#=> [["test", "blah"], ["foo", "bar bar bar"], ["test", "abc", "123", "456 789"]]

irb> s.split(/\s*\n\s*/).map do |line|
  line.sub(/^\s*\[\s*/,'').sub(/\s*\]\s*$/,'').split(/\s*\|\s*/)
end
#=> [["test", "blah"], ["foo", "bar bar bar"], ["test", "abc", "123", "456 789"]]

它们都是从分割新行开始(扔掉周围的空格)。

第一个然后通过查找不是[|]的任何内容来拆分每个块,然后抛弃额外的空格(调用strip on每个)。

然后第二个抛弃前导[并尾随](带空白),然后分割|(带空格)。


使用单个scan无法获得所需的最终结果。你能得到的最接近的是:

s.scan /\[(?:([^|\]]+)\|)*([^|\]]+)\]/
#=> [["test", " blah"], ["foo ", "bar bar bar"], ["123 ", " 456 789"]]

...丢弃信息,或者这个:

s.scan /\[((?:[^|\]]+\|)*[^|\]]+)\]/
#=> [["test| blah"], ["foo |bar bar bar"], ["test| abc |123 | 456 789"]]

...将每个“数组”的内容捕获为单个捕获,或者:

s.scan /\[(?:([^|\]]+)\|)?(?:([^|\]]+)\|)?(?:([^|\]]+)\|)?([^|\]]+)\]/
#=> [["test", nil, nil, " blah"], ["foo ", nil, nil, "bar bar bar"], ["test", " abc ", "123 ", " 456 789"]]

...最多硬编码为四个项目,并插入nil所需的.compact条目。

没有办法使用Ruby的scan来获取像/(?:(aaa)b)+/这样的正则表达式,并且每次重复匹配时都会获得多次捕获。

答案 2 :(得分:2)

为什么硬路径(单一正则表达式)?为什么不拆分的简单组合?以下是可视化过程的步骤。

str = "[test| blah] \n [foo |bar bar bar]\n[test| abc |123 | 456 789]"

arr = str.split("\n").map(&:strip) # => ["[test| blah]", "[foo |bar bar bar]", "[test| abc |123 | 456 789]"]
arr = arr.map{|s| s[1..-2] } # => ["test| blah", "foo |bar bar bar", "test| abc |123 | 456 789"]
arr = arr.map{|s| s.split('|').map(&:strip)} # => [["test", "blah"], ["foo", "bar bar bar"], ["test", "abc", "123", "456 789"]]

这可能远远低于scan,但至少它很简单:)

答案 3 :(得分:2)

“扫描,拆分,剥离和删除”火车残骸​​

整个前提似乎有缺陷,因为它假设您总是会在子数组中找到替换,并且表达式将不包含字符类。不过,如果这是你真正想要解决的问题,那么这应该做到。

首先,str.scan( /\[.*?\]/ )将为您提供三个数组元素,每个元素都包含伪数组。然后映射子数组,拆分交替字符。然后剥去子数组的每个元素的空格,并删除方括号。例如:

str = "[test| blah] \n [foo |bar bar bar]\n[test| abc |123 | 456 789]"
str.scan( /\[.*?\]/ ).map { |arr| arr.split('|').map { |m| m.strip.delete '[]' }}

#=> [["test", "blah"], ["foo", "bar bar bar"], ["test", "abc", "123", "456 789"]]

详细,循序渐进

映射嵌套数组并不总是直观的,因此我将上面的列车残骸解开为更多程序代码以进行比较。结果是相同的,但以下可能更容易推理。

string = "[test| blah] \n [foo |bar bar bar]\n[test| abc |123 | 456 789]"
array_of_strings = string.scan( /\[.*?\]/ )
#=> ["[test| blah]", "[foo |bar bar bar]", "[test| abc |123 | 456 789]"]

sub_arrays = array_of_strings.map { |sub_array| sub_array.split('|') }
#=> [["[test", " blah]"],
#    ["[foo ", "bar bar bar]"],
#    ["[test", " abc ", "123 ", " 456 789]"]]

stripped_sub_arrays = sub_arrays.map { |sub_array| sub_array.map(&:strip) }
#=> [["[test", "blah]"],
#    ["[foo", "bar bar bar]"],
#    ["[test", "abc", "123", "456 789]"]]

sub_arrays_without_brackets =
  stripped_sub_arrays.map { |sub_array| sub_array.map {|elem| elem.delete '[]'} }
#=> [["test", "blah"], ["foo", "bar bar bar"], ["test", "abc", "123", "456 789"]]