正则表达式中,lookaround 和 non-capture group 有什么功能上的区别?

10

我正在尝试举一个使用正向先行断言而非捕获组无法胜任的例子,以进一步了解它们的用法。我提出的所有示例都可以使用非捕获组,因此我感觉没有完全掌握正向先行断言的用法。

以下是一个字符串(摘自SO示例),其中答案中使用了正向先行断言。用户想要仅在第一列的值以ABC开头并且最后一列具有“active”值时抓取第二列的值。

string ='''ABC1    1.1.1.1    20151118    active
          ABC2    2.2.2.2    20151118    inactive
          xxx     x.x.x.x    xxxxxxxx    active'''
给出的解决方案使用了“正向先行断言”,但我注意到我可以使用非捕获组得出相同的答案。因此,我很难想出一个正向先行不起作用,非捕获组可以工作的示例。
pattern =re.compile('ABC\w\s+(\S+)\s+(?=\S+\s+active)') #solution

pattern =re.compile('ABC\w\s+(\S+)\s+(?:\S+\s+active)') #solution w/out lookaround

如果有人能够提供一个例子,我会非常感激。

谢谢。


这将与前瞻后面的内容有关。 前瞻是零宽度(我认为),而非捕获则不是。 - sniperd
2
一个组(捕获或非捕获)会消耗字符串。而环视则不会。 - cs95
1个回答

17

基本的区别在于非捕获组仍然会消耗它们匹配的字符串部分,从而使光标向前移动。

一个例子是当您尝试匹配某些由特定边界包围且这些边界可能重叠的字符串时,这将产生根本性的区别。样本任务:

从给定的字符串中匹配所有被b包围的a - 给定的字符串为bababaca。应该有两个匹配,位置分别是2和4。

使用lookaround(环视)很容易做到这一点,您可以使用b(a)(?=b)(?<=b)a(?=b)进行匹配。但是,(?:b)a(?:b)不起作用-第一个匹配还将消耗在第3个位置的b,这是第二个匹配所需的边界。(注意:在此处实际上并不需要非捕获组)

另一个比较突出的示例是密码验证-检查密码是否包含大写字母、小写字母、数字等-您可以使用一堆备选项来匹配这些内容,但是lookahead(环视)更容易实现:

(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?.])

对抗

(?:.*[a-z].*[A-Z].*[0-9].*[!?.])|(?:.*[A-Z][a-z].*[0-9].*[!?.])|(?:.*[0-9].*[a-z].*[A-Z].*[!?.])|(?:.*[!?.].*[a-z].*[A-Z].*[0-9])|(?:.*[A-Z][a-z].*[!?.].*[0-9])|...

这真的很有帮助。非常感谢!我有一点难以理解第二个例子。(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=[!?.])强制密码至少包含一个小写字母、一个大写字母、一个数字等等?由于环视不消耗任何东西,我们最终得到一个空匹配,对吧?我尝试在模式末尾添加'\S+',看看是否可以捕获字符串string = 'AZN###3232!abbb32.....'' 但是我最终得到了一个空匹配。我认为整个字符串都应该被捕获。 - Moondra
好的,我在上一个前瞻中忘记了 ".*"。是的,你可能想要将它与锚定和“.+”或“\S+”一起使用以匹配整个字符串。 - Sebastian Proske
2
不错的例子!...除了我们不应再检查密码格式之外:NIST已经不推荐这种做法 - NH.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接