正则表达式中的前瞻、后顾和原子组

554
我在我的正则表达式中找到了这些东西,但我不知道我可以用它们做什么。 有人有例子吗,这样我就可以尝试理解它们是如何工作的?
(?=) - positive lookahead
(?!) - negative lookahead
(?<=) - positive lookbehind
(?<!) - negative lookbehind

(?>) - atomic group

52
为什么正则表达式网站没有像这样简单的表格?相反,它们只有一大块文本来解释。http://www.regular-expressions.info/lookaround.html - Whitecat
11
好的,我会尽力进行翻译。以下是需要翻译的内容:@Whitecat Try: https://regex101.com http://www.regexr.com请使用:https://regex101.com 和 http://www.regexr.com 进行尝试。 - Andrew
5个回答

1621

例子

给定字符串foobarbarfoo:

bar(?=bar)     finds the 1st bar ("bar" which has "bar" after it)
bar(?!bar)     finds the 2nd bar ("bar" which does not have "bar" after it)
(?<=foo)bar    finds the 1st bar ("bar" which has "foo" before it)
(?<!foo)bar    finds the 2nd bar ("bar" which does not have "foo" before it)

您也可以将它们组合使用:

(?<=foo)bar(?=bar)    finds the 1st bar ("bar" with "foo" before it and "bar" after it)

定义

正向先行断言 (?=)

查找表达式A,后面跟随着表达式B:

A(?=B)

前瞻性否定(?!)

查找表达式 A,但不能后跟表达式 B:

A(?!B)

正向先行断言 (?<=)

查找满足条件 B 在表达式 A 前面的内容:

(?<=B)A

负向零宽断言(?<!)

查找表达式A,但不在表达式B之前出现:

(?<!B)A

原子组 (?>)

原子组会在组内第一个匹配项后退出组并忽略剩余的备选模式 (回溯被禁用)。

  • (?>foo|foot)s 应用于字符串 foots 时,将匹配第一个备选模式 foo,因为后面不是字母 s 而失败,回溯被禁用,故只返回这个结果。

非原子组会允许回溯;如果随后的匹配失败,它将回溯并使用备选模式,直到找到整个表达式的匹配或所有可能性都被耗尽。

  • (foo|foot)s 应用于字符串 foots 时,将:

    1. 匹配第一个备选模式 foo,因为后面不是字母 s 而失败,继而回溯至第二个备选模式;
    2. 匹配第二个备选模式 foot,因为后面是字母 s 而成功,故只返回这个结果。

相关资源

在线测试工具


1
“找到第二个栏”这部分是什么意思?表达式/字符串中只有一个栏。谢谢。 - ziggy
6
被测试的字符串是"foobarbarfoo"。你可以看到这个字符串中有两个"foo"和两个"bar"。 - skyfoot
5
请问何时需要使用原子组?如果我只需要与第一个备选项匹配,为什么要给出多个备选项呢? - arviman
5
以下是对 此答案 中关于原子组的更好解释:原子组是一种正则表达式语法,用于分组和捕获模式,并在匹配失败时防止回溯。 原子组以 (?>...) 的形式表示。当我们在表达式中使用括号时,这会创建一个捕获组,其中包含被括号包围的模式。 捕获组可以稍后在表达式中引用,并且还可以将其用作反向引用,并将其保留为匹配中的单独部分。 但是,如果我们只是需要一个分组而不需要捕获组,那么这个组可能会影响性能并导致回溯问题。原子组解决了这个问题。 它类似于普通的分组,但在匹配失败时它会防止回溯。 这意味着,在匹配失败时,原子组将保持匹配失败状态,而不是尝试回溯并寻找其他匹配。原子组非常适合处理长字符串和复杂模式,因为它们可以提高效率并避免回溯问题。 - Peter Krauss
16
请注意,这个回答对我来说非常重要,因为我参与了一个需要深入理解正则表达式的项目。这是一个优秀而简明的关于“look-around”的解释。 - Tom Coughlin
显示剩余4条评论

248

预测先行断言是零宽度断言,用于检查当前位置之后(或之前,基于正向或反向)是否存在一个正则表达式,当找到匹配时成功或失败(基于是否为正定向),并丢弃已匹配的部分。它们不会消耗任何字符 - 与其后面(如果有)的正则表达式匹配将从同一光标位置开始。

更多详细信息请阅读 regular-expression.info

  • 正向预测先行断言:

语法:

(?=REGEX_1)REGEX_2

只有当REGEX_1匹配成功时才会继续匹配;在匹配成功REGEX_1后,匹配结果被丢弃,然后从同一位置开始搜索REGEX_2。

例子:

(?=[a-z0-9]{4}$)[a-z]{1,2}[0-9]{2,3}

REGEX_1是[a-z0-9]{4}$,它匹配以四个字母或数字结尾的字符串。
REGEX_2是[a-z]{1,2}[0-9]{2,3},它匹配一个或两个字母后跟两个或三个数字。

REGEX_1确保字符串长度为4,但不会消耗任何字符,这使得搜索REGEX_2的起始位置与REGEX_1相同。现在,REGEX_2确保该字符串符合一些其他规则。如果没有前瞻,它将匹配长度为3或5的字符串。

  • 负向先行断言

语法:

(?!REGEX_1)REGEX_2

只有当 REGEX_1 无法匹配时才进行匹配;在检查 REGEX_1 后,从相同位置开始搜索 REGEX_2。

示例:

(?!.*\bFWORD\b)\w{10,30}$

前瞻部分检查字符串中是否包含FWORD,如果找到则失败,如果没有找到FWORD,则前瞻成功,接下来的部分验证字符串的长度在10到30之间,并且只包含字母数字字符a-zA-Z0-9_

后顾类似于前瞻:它只是查看当前光标位置之前的内容。一些正则表达式语言(例如JavaScript)不支持后顾断言。大多数支持后顾的语言(如PHP、Python等)要求后顾部分具有固定的长度。

  • 原子组基本上会在组内有一个令牌匹配时丢弃/忘记随后的所有令牌。请参阅此页面了解原子组示例。

根据您的解释,在JavaScript中似乎不起作用,/ (? = source) hello / .exec(“source ... hummhellosource”)= null。您的解释正确吗? - Helin Wang
@HelinWang 这个解释是正确的。你的正则表达式期望一个既是源字符串又是 hello 的字符串! - Amarghosh
@jddxf 能详细说明一下吗? - Amarghosh
@Amarghosh 我同意 "它们检查正则表达式(在当前位置左侧或右侧 - 根据前面或后面),当找到匹配项时成功或失败(根据是积极还是消极),并丢弃已匹配的部分。"。因此,正向先行断言应该检查当前位置右侧的正则表达式,其语法应为x(?=y)。 - jddxf
@Amarghosh 只有当 REGEX_2REGEX_1 后面时,(?=REGEX_1)REGEX_2 才匹配吗? - aandis

2

为什么 - 假设你正在玩Wordle,输入了“ant”。(是的三字母词,只是举个例子-冷静点)

答案返回为空白、黄色和绿色,你有一个三字母词列表想要使用正则表达式进行搜索,你会怎么做呢?

首先,你可以从第三个位置的t的存在开始:

[a-z]{2}t

我们可以通过指出我们没有一个a来改进。
[b-z]{2}t

我们可以进一步改进,说搜索必须包含一个n。
(?=.*n)[b-z]{2}t

或者来分解一下;

(?=.*n) - 向前查找,并检查匹配项中是否有一个n,它可能在n之前有零个或多个字符

[b-z]{2} - 在前两个位置上除了'a'以外的两个字母;

t - 第三个位置上确切地是一个't'


0
迅速掌握环视。 如何区分正向先行断言和反向先行断言? 跟我一起进行2分钟快速了解:
(?=) - positive lookahead
(?<=) - positive lookbehind

假设

    A  B  C #in a line

现在,我们问B,你在哪里?
B有两种方法来声明它的位置:

一、B在A前面,C在后面
二、B在C前面(向前看),A在后面(向后看)。

可以看出,在这两个解决方案中,“前面”和“后面”是相反的。
正则表达式是第二种解决方案。


我认为你搞反了:BA前面,BC后面。或者,CB前面,AB后面。难道我错过了什么吗? - Jon Grah

-1

我使用了“向后查找”来查找模式,并使用“向前负查找”来查找缺失的表(使用nolock)

expression="(?<=DB\.dbo\.)\w+\s+\w+\s+(?!with\(nolock\))"

matches=re.findall(expression,sql)
for match in matches:
    print(match)

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