在Python中获取正则表达式的所有匹配?

1 投票
3 回答
4489 浏览
提问于 2025-04-17 02:05

我有一个正则表达式,可以以多种重叠的方式匹配一个字符串。不过,它似乎只捕获了字符串中的一个匹配项,我该怎么才能获取所有可能的匹配呢?我试过用finditer,但没有成功,可能是我用错了。

我想解析的字符串是:

foo-foobar-foobaz

我使用的正则表达式是:

(.*)-(.*)

>>> s = "foo-foobar-foobaz"
>>> matches = re.finditer(r'(.*)-(.*)', s)
>>> [match.group(1) for match in matches]
['foo-foobar']

我想要匹配的是(foo和foobar-foobaz),但它似乎只得到了(foo-foobar和foobaz)。

3 个回答

1

如果你想检测重叠的匹配项,你需要自己实现这个功能。简单来说,对于一个字符串 foo,你可以按照以下步骤操作:

  1. 找到第一个匹配项,它从字符串的某个位置 i 开始。
  2. 然后再对 foo[i+1:] 这个部分运行匹配函数。
  3. 继续在字符串的剩余部分重复步骤1和2,每次都从新的位置开始。

如果你使用的是任意长度的捕获组(比如 (.*)),事情就会变得复杂一些。因为你可能不希望同时匹配到 foo-foobaroo-foobar,所以你需要做一些额外的分析。这样的话,你就不能每次只把 i 移动 +1,而是需要把它移动到第一个捕获组的长度加一的位置。

2

这不是正则表达式引擎通常能做到的事情。我不确定Python能不能做到,但Perl可以用以下方式实现:

local our @matches;
"foo-foobar-foobaz" =~ /
    ^(.*)-(.*)\z
    (?{ push @matches, [ $1, $2 ] })
    (*FAIL)
/xs;

这个具体的问题可能可以通过许多语言中的正则表达式引擎用以下技巧来解决:

my @matches;
while ("foo-foobar-foobaz" =~ /(?=-(.*)\z)/gsp) {
   push @matches, [ ${^PREMATCH}, $1 ];
}

(${^PREMATCH}指的是正则表达式匹配之前的内容,而$1指的是第一个()匹配到的内容。)

不过,你可以很容易地在正则表达式引擎之外解决这个具体的问题:

my @parts = split(/-/, "foo-foobar-foobaz");
my @matches;
for (1..$#parts) {
   push @matches, [
      join('-', @parts[0..$_-1]),
      join('-', @parts[$_..$#parts]),
   ];
}

抱歉使用了Perl的语法,但应该能让你明白这个思路。欢迎将其翻译成Python。

5

没问题:

>>> regex = "([^-]*-)(?=([^-]*))"
>>> for result in re.finditer(regex, "foo-foobar-foobaz"):
>>>     print("".join(result.groups()))
foo-foobar
foobar-foobaz

通过把第二个捕获括号放在一个前瞻断言中,你可以捕获它的内容,而不会影响整体匹配的结果。

我还用了[^-]*来代替.*,因为点号(.)也会匹配到分隔符-,而你可能不想要这个。

撰写回答