【问题标题】:Ruby Regex, get all possible matches (no clipping of the string)Ruby Regex,获取所有可能的匹配项(不剪切字符串)
【发布时间】:2013-04-29 02:13:48
【问题描述】:

我遇到了 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 版本中最后一个索引属性类似的东西。老实说,我认为它无论如何都不会起作用,因为我打算使用的正则表达式是递归的并且依赖于整个字符串,而该方法会切掉字符串。

【问题讨论】:

  • 所以基本上你想要一个字符串的排列?
  • 没有。这只是一个例子。实际的正则表达式有点难,但这是出现的问题。 scan 方法会切断它找到的字符串部分并继续。我需要它来保留字符串,以便下一个可能的匹配项可以查看它。
  • 我不确定,但第一个和第二个例子有点矛盾。按照第一个例子,我认为第二个应该返回[aa, aa, aa, aa, aa...., aaa, aaa, aaa ..., aaaa, aaaa, aaaa ...., aaaaa, aaaaa, aaaaaa]
  • 没有。那不是那个正则表达式寻找的东西。答案应该是aa, aaa。这些是唯一可能的匹配项。
  • Perl 可以做到这一点,但它可以非常迅速地爆炸。我需要类似的东西并放弃了。

标签: ruby regex


【解决方案1】:

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

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

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

扫描遍历每个字节,“正向预测”(?=) 在每一步测试正则表达式(..+)\1。 Look-aheads 不消耗字节,但它内部的捕获组返回匹配项(如果存在)。

【讨论】:

    【解决方案2】:

    您只是缺少第二个捕获组吗?

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

    您的期望似乎有问题。

    【讨论】:

    • 你知道吗。我才意识到我犯了一个小错误。
    • 但是建议的解决方案仍然不能解决原来的问题。该正则表达式也应该在aaa 上触发。
    • 我不知道你指的是哪个问题,但\1+ 永远不会匹配aaa(如果\1aa),只能是aa 的倍数。
    • @Cort3z 正则表达式不应捕获aaa,因为非贪婪修饰符? 表示它一看到aa 就会满足。
    • @Cort3z "aa" 是(..+?) 的捕获,"aaaa" 是(\1+) 的捕获。第一个是非贪婪的,第二个是贪婪的。
    【解决方案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+$/ 的非贪婪形式的期望解决方案是错误的,因为? 意味着它将对字符最少的模式感到满意。

    【讨论】:

    • 我收到undefined method 'lazy' for #&lt;Enumerator。也许我有不同版本的红宝石?我有 1.9.3p362
    • @Cort3z 正如我在答案中所说,lazy 是 ruby​​ 2.0 的一个特性。在 1.9 中,您可以省略它,它应该可以正常工作,只是产生更多的中间结果。
    【解决方案4】:

    我不明白为什么您的预期结果应该是这样的,但是对于只是从不同的起点应用正则表达式,就可以了。

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

    【讨论】:

      【解决方案5】:
      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(/../)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-09-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-11-18
        • 1970-01-01
        相关资源
        最近更新 更多