Ruby Regex,获取所有可能的匹配(不剪切字符串)

时间:2013-05-04 23:18:24

标签: ruby regex

我遇到了ruby正则表达式的问题。我需要找到所有(可能重叠)的匹配。这是问题的简化:

#Simple example
"Hey".scan(/../)
=> ["He"] 
#Actual results

#With overlapping matches the result should be
=> ["He"], ["ey"]

正在尝试执行的正则表达式并获得所有结果,如下所示:

"aaaaaa".scan(/^(..+)\1+$/) #This looks for multiples of (here) "a" bigger than one that "fills" the entire string. "aa"*3 => true, "aaa"*2 => true. "aaaa"*1,5 => false.
 => [["aaa"]] 

#With overlapping results this should be
 => [["aa"],["aaa"]]

是否有图书馆或方法在ruby中进行正则表达式以获得我追求的结果?

我发现了一些线索,这在Perl中是可能的,但经过数小时的研究后,我没有发现任何关于Ruby方法的事情。

但是我能够找到这个“Javascript Regex - Find all possible matches, even in already captured matches”,但我找不到类似Ruby的东西,也找不到类似于Ruby版本中最后一个索引属性的东西。说实话,我不认为它会起作用,因为我打算使用的正则表达式是递归的并且依赖于整个字符串,而那个方法会切断字符串。

5 个答案:

答案 0 :(得分:6)

有点老话题...... 不确定我是否理解,但我能找到的最好是:

"Hey".scan(/(?=(..))/)
 => [["He"], ["ey"]] 

"aaaaaa".scan(/(?=(..+)\1)/)
 => [["aaa"], ["aa"], ["aa"]] 

扫描遍历每个字节和正面预测" (?=)会在每一步中测试正则表达式(..+)\1。前瞻不消耗字节,但其中的捕获组返回匹配(如果存在)。

答案 1 :(得分:3)

您是否只是错过了第二个捕获组?

"aaaaaa".scan(/(..+?)(\1+)/)
#=> [["aa", "aaaa"]]

您的期望似乎有些不对劲。

答案 2 :(得分:3)

基于scan的任何解决方案的问题是找不到重叠匹配,因为scan始终会取得进展。有可能重新制作正则表达式,因此它完全嵌入零宽度正向前瞻,然后使用scan,但IIRC还有其他有效的正则表达式模式,这些模式在前瞻或后观中不起作用。

提出的问题存在一些含糊之处。这将该问题解释为真正要求查找正则表达式将匹配的目标字符串的所有唯一匹配子字符串。虽然不是绝对必要,但它使用ruby 2.0延迟评估来避免过多的中间数组分配。

class String
  def each_substring
    Enumerator.new do |y|
      (0...length).each do |b|
        (b...length).each do |e|
          y << self[b..e]
        end
      end
      y << '' 
    end
  end
end

class Regexp
  def all_possible_matches(str)
    str.each_substring.lazy.
    map { |s| match(s) }.
    reject(&:nil?).
    map { |m| m.size > 1 ? m[1..-1] : m[0] }.
    to_a.uniq
  end
end

/.{2,4}/.all_possible_matches('abcde')
=> ["ab", "abc", "abcd", "bc", "bcd", "bcde", "cd", "cde", "de"]

/^(..+?)\1+$/.all_possible_matches('aaaaaa')
=> [["aa"]]
/^(..+)\1+$/.all_possible_matches('aaaaaa')
=> [["aa"], ["aaa"]]
/^(..+?)\1+$/.all_possible_matches('aaaaaaaaa')
=> [["aa"], ["aaa"]]
/^(..+)\1+$/.all_possible_matches('aaaaaaaaa')
=> [["aa"], ["aaa"], ["aaaa"]]

编辑:使其在存在时返回捕获组。 OP对非贪婪形式/^(..+?)\1+$/的理想解决方案是错误的,因为?意味着它将满足最少字符的模式。

答案 3 :(得分:1)

我不明白为什么你的预期结果应该是那样,但是只是从不同的起点应用正则表达式,这样做。

class String
  def awesome_regex_scan r
    (0...length).map{|i| match(r, i)}.map(&:to_a).reject(&:empty?).uniq
  end
end

"Hey".awesome_regex_scan(/../) # => [["He"], ["ey"]]

如上所述,它与您的预期结果不符,我不明白为什么您期望您做的事情:

"aaaaaa".awesome_regex_scan(/^(..+?)\1+$/) # => [["aaaaaa", "aa"]]
"aaaaaa".awesome_regex_scan(/^(..+)\1+$/) # => [["aaaaaa", "aaa"]]

答案 4 :(得分:0)

class String
  def awesome_regex_scan(pattern)
    result = []
    source = self
    while (match = source.match(pattern))
      result << match.to_s
      source = source.slice(match.begin(0)+1..-1)
    end
    result
  end
end

p "Hey".awesome_regex_scan(/../)