regex 为什么正则表达式的“非捕获”组不工作?

0sgqnhkj  于 2023-02-10  发布在  其他
关注(0)|答案(7)|浏览(184)

在下面的代码片段中,**非捕获组"(?:aaa)"**应该在匹配结果中忽略。
结果只能是"_bbb"
但是,我在匹配结果中得到了"aaa_bbb";只有当我指定group(2)时,它才会显示"_bbb"

>>> import re
>>> s = "aaa_bbb"
>>> print(re.match(r"(?:aaa)(_bbb)", s).group())

aaa_bbb
cnwbcb6i

cnwbcb6i1#

我想你误解了“非捕获组”的概念,由非捕获组匹配的文本仍然是整个正则表达式匹配的一部分。
正则表达式(?:aaa)(_bbb)和正则表达式(aaa)(_bbb)都返回aaa_bbb作为总体匹配,区别在于第一个正则表达式有一个捕获组,返回_bbb作为匹配,而第二个正则表达式有两个捕获组,返回aaa_bbb作为各自的匹配,在Python代码中,要获得_bbb,你需要对第一个正则表达式使用group(1),对第二个正则表达式使用group(2)
非捕获组的主要好处是,你可以将它们添加到正则表达式中,而不会打乱正则表达式中捕获组的编号。它们还提供了(稍微)更好的性能,因为正则表达式引擎不必跟踪非捕获组匹配的文本。
如果你真的想把aaa从整个正则表达式匹配中排除,那么你需要使用lookaround。在这种情况下,正向后查找可以达到目的:(?<=aaa)_bbb。使用这个正则表达式,group()在Python中返回_bbb。不需要捕获组。
我的建议是,如果您有能力使用捕获组来获取正则表达式匹配的一部分,请使用该方法而不是lookaround。

gt0wga4j

gt0wga4j2#

group()group(0)将返回整个匹配。后续组是实际的捕获组。

>>> print (re.match(r"(?:aaa)(_bbb)", string1).group(0))
aaa_bbb
>>> print (re.match(r"(?:aaa)(_bbb)", string1).group(1))
_bbb
>>> print (re.match(r"(?:aaa)(_bbb)", string1).group(2))
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
IndexError: no such group

如果您想要与group()相同的行为:
" ".join(re.match(r"(?:aaa)(_bbb)", string1).groups())

l5tcr1uw

l5tcr1uw3#

试试看:

print(re.match(r"(?:aaa)(_bbb)", string1).group(1))

group()group(0)相同,并且组0始终存在,这是整个RE匹配。

omhiaaxx

omhiaaxx4#

TFM:
class re.MatchObject
group([group1, ...])
返回匹配项的一个或多个子组。如果有单个参数,则结果为单个字符串;如果有多个参数,则结果是一个元组,每个参数一个项目。如果没有参数,group 1默认为零(返回整个匹配项)。如果groupN参数为零,则相应的返回值为整个匹配字符串。

t0ybt7op

t0ybt7op5#

必须指定group(1)才能只获取括号中的部分(本例中为_bbb)。
不带参数的group()将返回与完整正则表达式匹配的整个字符串,而不管它的某些部分是否由括号额外捕获。

uubf1zoe

uubf1zoe6#

在match对象上使用groups方法而不是group。它返回所有捕获缓冲区的列表。没有参数的group方法返回正则表达式的整个匹配。

hfsqlsce

hfsqlsce7#

让我们一起来看看扬·戈瓦尔特的回答:
非捕获组的主要好处是,您可以将它们添加到正则表达式中,而不会打乱正则表达式中捕获组的编号。
为什么要添加一个不会打乱组编号的组呢?

OP的例子r"(?:aaa)(_bbb)"吸引了我的注意力(和大脑)到"aaa",但它没有什么特别之处......对我来说,它只是看起来与真正重要的东西"_bbb"相邻。
我认为它实际上应该是其中之一:

  • r"aaa(_bbb)":如果"aaa"对您/我们如何读取正则表达式不重要,则"aaa"对我们没有意义
  • r"(?:aaa(_bbb))":如果"aaa_bbb"是一个单独的东西,我们应该(在心理上)作为一个整体来考虑,但最终我们只使用"_bbb"

从正则表达式引擎的Angular 来看,这三个函数都是等效的:

s = "aaa_bbb"
for pattern in [r"(?:aaa)(_bbb)", r"aaa(_bbb)", r"(?:aaa(_bbb))"]:
    m = re.match(pattern, s)

    print(pattern)
    print("=" * len(pattern))
    print(f"groups(): {m.groups()}")
    for i in range(1, len(m.groups()) + 1):
        print(f"  group({i}): {m.group(i)}")
    print()
(?:aaa)(_bbb)
=============
groups(): ('_bbb',)
  group(1): _bbb

aaa(_bbb)
=========
groups(): ('_bbb',)
  group(1): _bbb

(?:aaa(_bbb))
=============
groups(): ('_bbb',)
  group(1): _bbb

对于一个真实的例子,我想解析一个类Unix的time命令的输出:

% /usr/bin/time -l foo
...
        9.87 real         6.54 user         3.21 sys
...

我最初的Python正则表达式是:

import re

line = "        9.87 real         6.54 user         3.21 sys"

pattern = r"\s+(\d+\.\d+) real\s+(\d+\.\d+) user\s+(\d+\.\d+) sys"

这就给了我正确的分组:

m = re.match(pattern, line)

print(m.groups())  # ('9.87', '6.54', '3.21')
print(m.group(1))  # 9.87
print(m.group(2))  # 6.54
print(m.group(3))  # 3.21

但是值末尾的标签一直把我的眼睛抛开,我一直看到"real\s+(\d +.\d+)"而不是"(\d +.\d+)real"。
我可以尝试添加括号来直观地区分"元组",这在视觉上对我来说稍微好一点:

pattern = r"\s+((\d+\.\d+) real)\s+((\d+\.\d+) user)\s+((\d+\.\d+) sys)"

但这打破了我想要的分组逻辑:

m = re.match(pattern, line)

print(m.groups())  # ('9.87 real', '9.87', '6.54 user', '6.54', '3.21 sys', '3.21')
print(m.group(1))  # 9.87 real
print(m.group(2))  # 9.87
print(m.group(3))  # 6.54 user
print(m.group(4))  # 6.54
print(m.group(5))  # 3.21 sys
print(m.group(6))  # 3.21

让这些元组 * 不分组 * 让我的元组有了更好的视觉效果(我的眼睛仍然会来回移动,但至少有了清晰的边界):

pattern = r"\s+(?:(\d+\.\d+) real)\s+(?:(\d+\.\d+) user)\s+(?:(\d+\.\d+) sys)"

并且不考虑分组的逻辑

m = re.match(pattern, line)

print(m.groups())  # ('9.87', '6.54', '3.21')
print(m.group(1))  # 9.87
print(m.group(2))  # 6.54
print(m.group(3))  # 3.21

相关问题