python 正则表达式匹配搜索字符串的每个索引不止一次

python regex match more than once per index of search string

我正在寻找一种方法来使 python re 模块或更新的 regex 模块的 finditer 函数匹配特定模式的所有可能变化,重叠或其他。我知道在不使用搜索字符串的情况下使用前瞻来获得匹配项,但我仍然只为每个索引获取一个正则表达式,而我可以获得多个。

我使用的正则表达式是这样的:

(?=A{2}[BA]{1,6}A{2})

所以在字符串中:

AABAABBAA

应该可以匹配:

AABAA AABAABBAA AABBAA

但目前它只会匹配其中的最后两个。我意识到这与 [BA]{1,6} 的贪婪有关。有没有办法让正则表达式匹配从最懒惰到最贪婪的所有可能模式?

这个正则表达式正确匹配了 3 个字符串:

AABAA AABAABBAA AABBAA

((^(AA){1}(BAA)$){1})|(((AA){1}(BB){1}(AA){1}$){1})

如果你想用 python 在字符串 "AABAABBAA" 中搜索这些子字符串,你可以使用方法 "search" :

re.search('AABAABB','AABAABBAA')
re.search('AABAABBAA','AABAABBAA')
re.search('AABBAA','AABAABBAA')

您将无法使用 finditer() 或任何正常的匹配导航方式。我管理它的唯一方法(在 Java,而不是 Python 中)是手动迭代源字符串的每个唯一子字符串(由起始位置和长度确定),将正则表达式应用于每个子字符串以正常方式(不使用环顾四周),并固定在两端。

Java 的匹配器 class 通过其 region() 方法提供了一种方便的方法。它使您可以假装源的任何子字符串实际上是整个内容,而不必生成大量新的 String 对象。 matches() 方法会自动在两端锚定匹配项,因此您根本不必修改正则表达式。

我很确定 Python 的 re 模块不支持这样的功能,但是另一个模块中可能有一些东西可以用来达到类似的效果(我是不流利 Python,所以这只是乐观)。我对 regex 模块更不熟悉,但它似乎支持所有其他风格的所有最甜蜜的功能,所以值得一看。

这是我的 Java 解决方案,如果您感兴趣的话:

public static void printAllMatches(String text, String regex)
{
  System.out.printf("%s results:%n", text);
  Matcher m = Pattern.compile(regex).matcher(text);
  int end = text.length();
  for (int i = 0; i < end; ++i)
  {
    for (int j = i + 1; j <= end; ++j) 
    {
      m.region(i, j);

      if (m.matches()) 
      {
        for (int k = 0; k < i; k++)
        {
          System.out.print(" ");
        }
        System.out.println(m.group());
      }   
    }   
  }   
}

输出:

AABAABBAA results:
AABAA
AABAABBAA
   AABBAA

I realise it is to do with the greediness of the [BA]{1,6}. Is there a way to make the regex match everything from the laziest to the greediest possible pattern?

问题是双重的。

1. Regex engines will only match once at a character position.
2. There is not a regex construct of between lazy and greedy  
   it's either one or the other.  

暂时跳过问题 1..,

问题2:
可能存在 {1,6} 1,2,3,4,5 或 6 个匹配
的情况 在给定位置的构造(字符)。

要解决该问题,您必须指定独立的 {1}、{2}、{3}、{4}、{5}、{6}
作为该位置的可选交替。
显然 range {1,6} 是行不通的。

就一个Range而言,可以指定找到
通过添加惰性修饰符的最小数量 {1,6}?
但这只会找到它能找到的最小数量,不多也不少。

最后,

问题 1:
当正则表达式引擎匹配时,它总是将当前位置向前推进
数量等于最后一场比赛的长度。
在匹配零长度断言的情况下,人为增加
向前一个字符的位置。


所以,鉴于这两个问题,可以用这些strengths/weaknesses来
想出一个解决方法,并且不得不忍受一些副作用。

解决方法
将所有可能的选择放在一个位置作为要分析的断言。 一个位置的每个匹配项都将包含一个包含变体的组列表。
因此,如果您匹配了 6 个可能变体组中的 3 个变体,则具有值的组将是变体。

如果 none 个组有值,则在该位置未找到变体。
不会发生任何变体,因为 所有 断言都是可选的。
为了避免在这些特定位置进行不必要的匹配,final
条件 可用于不报告这些。 (即 (?(1)|(?(2)|(?!))) 等等)。


让我们以您的范围为例。
我们将在最后使用条件来验证匹配的组,
但没有它也可以完成。
_请注意,使用此 range 示例会导致与 identical
重叠 在最后一场比赛中的价值。这确保在
处的唯一匹配 一个位置(下面的示例显示了如何避免这种情况)。

 # (?=(A{2}[BA]{1,6}?A{2}))?(?=(A{2}[BA]{1,6}A{2}))?(?(1)|(?(2)|(?!)))

 (?=
      (                             # (1 start)
           A{2}
           [BA]{1,6}? 
           A{2} 
      )                             # (1 end)
 )?
 (?=
      (                             # (2 start)
           A{2}
           [BA]{1,6} 
           A{2} 
      )                             # (2 end)
 )?
 (?(1)
   |  (?(2)
        |  (?!)
      )
 )

输出:

 **  Grp 1 -  ( pos 0 , len 5 ) 
AABAA  
 **  Grp 2 -  ( pos 0 , len 9 ) 
AABAABBAA  

-------------

 **  Grp 1 -  ( pos 3 , len 6 ) 
AABBAA  
 **  Grp 2 -  ( pos 3 , len 6 ) 
AABBAA  

相同,但没有 范围 问题。
在这里,我们明确定义了独特的结构。
记下每个位置的唯一值。

 # (?=(A{2}[BA]{1}A{2}))?(?=(A{2}[BA]{2}A{2}))?(?=(A{2}[BA]{3}A{2}))?(?=(A{2}[BA]{4}A{2}))?(?=(A{2}[BA]{5}A{2}))?(?=(A{2}[BA]{6}A{2}))?(?(1)|(?(2)|(?(3)|(?(4)|(?(5)|(?(6)|(?!)))))))

 (?=
      (                             # (1 start)
           A{2}
           [BA]{1} 
           A{2} 
      )                             # (1 end)
 )?
 (?=
      (                             # (2 start)
           A{2}
           [BA]{2} 
           A{2} 
      )                             # (2 end)
 )?
 (?=
      (                             # (3 start)
           A{2}
           [BA]{3} 
           A{2} 
      )                             # (3 end)
 )?
 (?=
      (                             # (4 start)
           A{2}
           [BA]{4} 
           A{2} 
      )                             # (4 end)
 )?
 (?=
      (                             # (5 start)
           A{2}
           [BA]{5} 
           A{2} 
      )                             # (5 end)
 )?
 (?=
      (                             # (6 start)
           A{2}
           [BA]{6} 
           A{2} 
      )                             # (6 end)
 )?

 (?(1)|(?(2)|(?(3)|(?(4)|(?(5)|(?(6)|(?!)))))))

输出:

 **  Grp 1 -  ( pos 0 , len 5 ) 
AABAA  
 **  Grp 2 -  NULL 
 **  Grp 3 -  NULL 
 **  Grp 4 -  NULL 
 **  Grp 5 -  ( pos 0 , len 9 ) 
AABAABBAA  
 **  Grp 6 -  NULL 

------------------

 **  Grp 1 -  NULL 
 **  Grp 2 -  ( pos 3 , len 6 ) 
AABBAA  
 **  Grp 3 -  NULL 
 **  Grp 4 -  NULL 
 **  Grp 5 -  NULL 
 **  Grp 6 -  NULL 

最后,你需要做的就是在每场比赛中,抓住捕获组
带有值,并将它们放入数组中。