非捕获组是否多余?

Are non-capturing groups redundant?

可选的非捕获组是否多余?

是以下正则表达式:

(?:wo)?men

在语义上等同于以下正则表达式?

(wo)?men

你的 (?:wo)?men(wo)?men 在语义上是等价的,但在技术上是不同的,即第一个使用非捕获组,另一个使用捕获组。因此,问题是为什么在我们有捕获组时使用非捕获组

非捕获组有时会有帮助。

  1. 避免过多的反向引用(请记住,有时很难使用高于 9 的反向引用)
  2. 为了避免 99 个编号的反向引用限制的问题(通过减少编号的捕获组的数量)(来源:Regular-expressions.info大多数正则表达式支持最多 99 个捕获组和双-数字反向引用。)
    注意 这不属于 Java 正则表达式引擎,也不属于 PHP 或 .NET 正则表达式引擎.
  3. lessen the overhead 由于将捕获存储在堆栈中
  4. 我们可以在不破坏捕获组顺序的情况下向现有正则表达式添加更多分组。

此外,它只是 makes our matches cleaner:

You can use a non-capturing group to retain the organisational or grouping benefits but without the overhead of capturing.

重构现有的正则表达式以将捕获组转换为非捕获组似乎不是一个好主意,因为它 may ruin the code 或者需要太多的努力。

其他地方的一个问题也在问同样的问题,我在 Python 中提供了一个示例的答案:

它并不“具有相同的效果”——在一种情况下,组被捕获并可访问,在另一种情况下,它仅用于完成匹配。

当人们对访问组的值不感兴趣时​​,他们会使用 non-capturing 组 - 以在有很多匹配项的情况下保存 space,而且在正则表达式引擎的情况下也能获得更好的性能针对它进行了优化。

Python中的一个无用的例子来说明这一点:

from timeit import timeit
import re

chars = 'abcdefghij'
s = ''.join(chars[i % len(chars)] for i in range(100000))


def capturing():
    re.findall('(a(b(c(d(e(f(g(h(i(j))))))))))', s)


def noncapturing():
    re.findall('(?:a(?:b(?:c(?:d(?:e(?:f(?:g(?:h(?:i(j))))))))))', s)


print(timeit(capturing, number=1000))
print(timeit(noncapturing, number=1000))

输出:

5.8383678999998665
1.0528525999998237

注意:尽管有 PyCharm(如果您碰巧使用它)警告“不必要的 non-capturing 组”- 警告是正确的,但显然不是全部事实。逻辑上不需要,但绝对没有同样的实际效果。

如果您想要摆脱它们的原因是为了抑制此类警告,PyCharm 允许您这样做:

# noinspection RegExpUnnecessaryNonCapturingGroup
re.findall('(?:a(?:b(?:c(?:d(?:e(?:f(?:g(?:h(?:i(j))))))))))', s)

迂腐的另一个注意事项:上面的例子在逻辑上也不是严格等价的。但是它们匹配相同的字符串,只是结果不同。

c = re.findall('(a(b(c(d(e(f(g(h(i(j))))))))))', s)
nc = re.findall('(?:a(?:b(?:c(?:d(?:e(?:f(?:g(?:h(?:i(j))))))))))', s)

c 是 10 元组列表 ([('abcdefghij', 'bcdefghij', ..), ..]),而 nc 是单个字符串列表 (['j', ..])。