我对正则表达式非常体面,现在我再次尝试了解前瞻和后瞻性断言。它们大多有意义,但我不太确定订单如何影响结果。我一直在看this site,它在表达式之前放置了lookbehinds,并在表达式之后进行了预测。我的问题是,这会改变什么吗?最近关于SO的答案在表达之前放置了前瞻,这导致了我的困惑。
答案 0 :(得分:9)
当教程介绍了lookarounds时,他们倾向于为每个用例选择最简单的用例。因此,他们会使用(?<!a)b
('b'前面没有'a')或q(?=u)
('q'后跟'u')等示例。这只是为了避免使用令人分心的细节混淆解释,但它往往会产生(或强化)外观和前瞻应该以某种顺序出现的印象。我花了很长时间来克服这个想法,而且我也见过其他几个受其影响的人。
试着看一些更现实的例子。出现的一个问题涉及验证密码;例如,确保新密码长度至少为六个字符,并且至少包含一个字母和一个数字。一种方法是:
^(?=.*[A-Za-z])(?=.*\d)[A-Za-z0-9]{6,}$
字符类[A-Za-z0-9]{6,}
可以匹配所有字母或所有数字,因此您使用前瞻以确保每个字符中至少有一个。在这种情况下,你必须首先进行前瞻 ,因为正则表达式的后面部分必须能够检查整个字符串。
再举一个例子,假设你需要找到所有出现的单词“there”,除非它前面带有引号。显而易见的正则表达式是(?<!")[Tt]here\b
,但如果您正在搜索大型语料库,则可能会产生性能问题。如上所述,正则表达式将在文本中的每个位置执行负面的lookbehind,并且只有在成功时才会检查正则表达式的其余部分。
每个正则表达式引擎都有自己的优点和缺点,但有一点是真实的,就是它们能够更快地找到固定的文字字符序列而不是其他任何东西 - 序列越长越好。这意味着可以更快地执行lookbehind last ,即使这意味着匹配单词两次:
[Tt]here\b(?<!"[Tt]here)
因此,规定外观的规则是没有规则;你把它们放在每种情况下最有意义的地方。
答案 1 :(得分:4)
我认为,在一个例子中展示比解释更容易。我们来看看这个正则表达式:
(?<=\d)(?=(.)\1)(?!p)\w(?<!q)
这意味着:
(?<=\d)
- 确保匹配位置前的数字为数字。(?=(.)\1)
- 确保我们在此(相同)位置匹配的任何字符后跟其自身的副本(通过反向引用)。(?!p)
- 确保以下内容不是p
。\w
- 匹配字母,数字或下划线。请注意,这是我们第一次实际匹配并使用该角色。(?<!q)
- 确保我们目前匹配的内容不会以q
结尾。所有这些都会匹配abc5ddx
或9xx
但不是5d
或6qq
或asd6pp
或add
等字符串。请注意,每个断言都是独立工作的。它只是停下来,环顾四周,如果一切顺利,就可以继续匹配。
另请注意,在大多数(可能是所有)实现中,lookbehinds具有固定长度的限制。您不能在其中使用?
,*
和+
等重复/可选操作符。这是因为要匹配一个模式,我们需要一个起点 - 否则我们必须尝试匹配字符串中每个点的每个lookbehind。
此正则表达式在字符串a3b5ddx
上的示例运行如下:
\d
总是匹配1个字符)。我们无法匹配负指数,因此失败并推进光标。a
与\d
不匹配,因此失败并再次前进光标。3
匹配\d
,因此请保持光标不变并继续匹配。b
匹配(.)
并被捕获。 5
与\1
(已捕获的b
)不匹配。因此,失败并前进光标。b
与\d
不匹配,因此失败并再次前进光标。5
匹配\d
,因此请保持光标不变并继续匹配。d
匹配(.)
并被捕获。第二个d
与\1
匹配(这是第一个被捕获的d
)。允许匹配从我们中断的地方继续。b
与p
不匹配,因为这是一个负面的预测,这就是我们想要的;允许匹配继续。\w
b
匹配。因为我们已经消耗了一个角色并继同时将此标记为比赛的开始。q
总是匹配1个字符)。 d
与q
不匹配,这是我们想要的负面看法。d
),意识到我们正处于正则表达式的末尾并报告成功。答案 2 :(得分:1)
1(?=ABC)
表示 - 查找1
,然后匹配(但不捕获)ABC
。
(?<=ABC)1
表示 - 在当前位置之前匹配(但不捕获)ABC
,并继续匹配1
。
所以,通常情况下,你会在表达式之后放置前瞻,并将它放在它之前。
当我们在表达式后面放置一个lookbehind时,我们重新检查我们已匹配的字符串。当您有复杂的条件时(通常可以将其视为正则表达式的AND
),这种情况很常见。例如,请查看Daniel Brückner最近的答案:
.&.(?<! & )
首先,捕获两个字符之间的&符号。接下来,你检查它们都不是空格(\S&\S
在这里不起作用,OP想要捕获1&_
)。