我想迭代一个带有正则表达式模式的字符串。我需要迭代匹配以及它们之间的非匹配,并在迭代时访问匹配信息。
如果我不需要访问不匹配项,那么我可以使用String#scan
执行此操作:
"some string".scan(/(some pattern)|(another pattern)/) do
if $1 then ...
elsif $2 then ...
end
end
但我也需要迭代不匹配的部分,所以我可能需要使用String#split
。但String#split
不会阻止,如果我之后使用each
:
"some string".split(/((some pattern)|(another pattern))/).each do
...
end
然后,我无法访问块中的匹配信息。我想做点什么:
"some string".split(/((some pattern)|(another pattern))/) do
if $2 then ...
elsif $3 then ...
else ... # access the non-matching part
end
end
有没有办法在使用String#split
进行迭代时访问最后一个匹配信息?
我可以使用scan
并在正则表达式的末尾添加|(.*?)
来强制它:
"some string".scan(/(some pattern)|(another pattern)|(.*?)/) do
if $1 then ...
elsif $2 then ...
elsif $3 then ...
end
end
但是使用非贪婪的匹配是非常低效的,我不能使用它。
答案 0 :(得分:2)
我提出了在每个匹配周期迭代非匹配部分和匹配部分的想法。这使用scan
代替split
,并且将实现此目的。
s = "some string"
i = 0
s.scan(/(some pattern)|(another pattern)|\z/) do
# Do something with the non-matching part
... s[i...$~.begin(0)] ... # This corresponds to the string in between
i = $~.end(0)
# Do something with the matching part
if $1 then ...
elsif $2 then ...
end
end
答案 1 :(得分:2)
如果您只使用match
一次处理字符串一次匹配,而不是像scan
那样一次处理所有字符串,则可以将pre_match
中的数据注入结果:
def match_all(s, r)
match = s.match(r)
if match
pre_captures = [match.pre_match] + match.captures.map{nil}
captures = [nil] + match.captures
[pre_captures, captures] + match_all(match.post_match, r)
else
[[s]]
end
end
此代码将输入字符串转换为表示[unmatched data, first match group, second match group, etc...]
的元组,然后可以根据需要迭代数据:
match_all("the match information in the block", /(at)|(in)/).each do |a, b, c|
if a
puts "(pre: #{a})"
elsif b
puts "(1st: #{b})"
elsif c
puts "(2nd: #{c})"
end
end
哪个输出:
(pre: the m)
(1st: at)
(pre: ch )
(2nd: in)
(pre: form)
(1st: at)
(pre: ion )
(2nd: in)
(pre: the block)
同样的功能也可以迭代实现,如下所示:
def match_all_iter(s, r)
s_mut = s
all_captures = []
loop do
match = s_mut.match(r)
break unless match
pre_captures = [match.pre_match] + match.captures.map{nil}
captures = [nil] + match.captures
all_captures += [pre_captures, captures]
s_mut = match.post_match
end
all_captures += [[s_mut]]
end