Python re.finditer 的 match.groups() 不包含所有匹配组

3 投票
2 回答
7361 浏览
提问于 2025-04-18 02:41

我正在尝试在Python中使用正则表达式(regex)来查找并打印出多行文本中所有匹配的行。

我搜索的文本可能有以下这种结构:

AAA
ABC1
ABC2
ABC3
AAA
ABC1
ABC2
ABC3
ABC4
ABC
AAA
ABC1
AAA

我想从中提取出那些至少出现一次并且前面有AAAABC*

问题是,尽管我能通过分组捕获到我想要的内容:

match = <_sre.SRE_Match object; span=(19, 38), match='AAA\nABC2\nABC3\nABC4\n'>

... 但我只能访问到这个分组的最后一个匹配项:

match groups = ('AAA\n', 'ABC4\n')

下面是我为解决这个问题而使用的示例代码。

#! python
import sys
import re
import os

string = "AAA\nABC1\nABC2\nABC3\nAAA\nABC1\nABC2\nABC3\nABC4\nABC\nAAA\nABC1\nAAA\n"
print(string)

p_MATCHES = []
p_MATCHES.append( (re.compile('(AAA\n)(ABC[0-9]\n){1,}')) ) #   
matches = re.finditer(p_MATCHES[0],string)

for match in matches:
    strout = ''
    gr_iter=0
    print("match = "+str(match))
    print("match groups = "+str(match.groups()))
    for group in match.groups():
    gr_iter+=1
    sys.stdout.write("TEST GROUP:"+str(gr_iter)+"\t"+group) # test output
    if group is not None:
        if group != '':
            strout+= '"'+group.replace("\n","",1)+'"'+'\n'
sys.stdout.write("\nCOMPLETE RESULT:\n"+strout+"====\n")

2 个回答

0

你想要找到在一个AAA\n后面紧接着出现的连续ABC\n的模式,并且希望这个匹配方式尽可能贪婪。也就是说,你只想要连续的ABC\n,而不是包含最近的ABC\n的元组。所以在你的正则表达式中,要排除掉子组。

AAA\n(ABC[0-9]\n)+

然后用括号()来捕获你感兴趣的部分,同时记得要排除子组。

AAA\n((?:ABC[0-9]\n)+)

接下来,你可以使用findall()或者finditer()。我觉得finditer更简单,特别是当你需要捕获多个部分的时候。finditer的用法是:

import re
matches_iter = re.finditer(r'AAA\n((?:ABC[0-9]\n)+)', string)

[print(i.group(1)) for i in matches_iter]

findall使用了原来的{1,},因为它是+的更详细的写法:

matches_all = re.findall(r'AAA\n((?:ABC[0-9]\n){1,})', string)

[[print(x) for x in y.split("\n")] for y in matches_all]
7

这里是你的正则表达式:

(AAA\r\n)(ABC[0-9]\r\n){1,}

正则表达式可视化

Debuggex 演示

你的目标是捕捉所有紧跟在 AAA 后面的 ABC#。在这个 Debuggex 演示中,你可以看到所有的 ABC# 确实都被匹配到了(它们被高亮显示为黄色)。但是,由于只有“被重复的部分”

ABC[0-9]\r\n

捕捉(在括号内),而它的量词

{1,}

没有被捕捉到,这就导致所有匹配项除了最后一个都被丢弃了。为了获取它们,你还必须捕捉到量词:

AAA\r\n((?:ABC[0-9]\r\n){1,})

正则表达式可视化

Debuggex 演示

我把“被重复的部分”(ABC[0-9]\r\n)放进了一个非捕捉组里。(我也停止捕捉 AAA,因为你似乎不需要它。)

捕捉到的文本可以按换行符分割,这样就能得到你想要的所有部分。

(注意,单独的 \n 在 Debuggex 中不起作用。它需要\r\n。)


这是一种变通方法。并不是所有的正则表达式都能支持重复捕捉的迭代(哪些可以呢...?)。更常见的方法是循环遍历并处理每个找到的匹配项。这里有一个来自 Java 的例子:

   import java.util.regex.*;

public class RepeatingCaptureGroupsDemo {
   public static void main(String[] args) {
      String input = "I have a cat, but I like my dog better.";

      Pattern p = Pattern.compile("(mouse|cat|dog|wolf|bear|human)");
      Matcher m = p.matcher(input);

      while (m.find()) {
         System.out.println(m.group());
      }
   }
}

输出:

cat
dog

(来自 http://ocpsoft.org/opensource/guide-to-regular-expressions-in-java-part-1/,大约在1/4处)


请考虑将Stack Overflow 正则表达式常见问题解答添加到书签,以便将来参考。这个答案中的链接都来自于此。

撰写回答