Ruby扫描正则表达式

4

我正在尝试分割字符串:

"[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"]
# ]

我需要在每个竖线处分割,而不是第一个竖线处。应该使用什么正则表达式才能实现这一点?

可能是重复的问题:Ruby .split() 正则表达式 - sawa
4个回答

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

1
这是最好的答案。它在正确的位置使用了scansplit - sawa
所有的答案都很好,但这似乎是最简单的解决方案。 - Ryan King

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函数)。

第二种方法然后会删除前导的[和尾随的](包括空格),然后按照|进行拆分(包含空格)。


你无法只使用一个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"]]

这段代码被硬编码为最多四个项,并插入了需要使用 .compact 去除的 nil 条目。

无法使用 Ruby 的 scan 方法,对于像 /(?:(aaa)b)+/ 这样的正则表达式,每次匹配重复部分都会获得多个捕获。


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高效,但至少它很简单 :)

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"]]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接