正则表达式中“后向断言必须是固定长度”的技术原因是啥?

Posted

技术标签:

【中文标题】正则表达式中“后向断言必须是固定长度”的技术原因是啥?【英文标题】:What's the technical reason for "lookbehind assertion MUST be fixed length" in regex?正则表达式中“后向断言必须是固定长度”的技术原因是什么? 【发布时间】:2011-04-17 07:43:51 【问题描述】:

例如下面的正则表达式会导致报错lookbehind assertion is not fixed length

#(?<!(?:(?:src)|(?:href))=["\']?)((?:https?|ftp)://[^\s\'"<>()]+)#S

这种限制对于前瞻是不存在的。

【问题讨论】:

您使用什么参考来表示“lookbehind assertion MUST be fixed length”? lookbehind assertion is not fixed length会导致失败,我们不能从中推断吗? 您使用的是什么正则表达式引擎?珀尔? C#? php?有很多工具可以处理正则表达式,并且都有自己的怪癖 我正在使用 PCRE。 IMO,lookaheadlookbehind 应该是相似的,为什么这里不同? 我读过,lookaround只是lookahead和lookbehind的缩写。但我的问题仍然没有回答。 【参考方案1】:

Lookahead 和lookbehind 并不像它们的名字所暗示的那样相似。前瞻表达式的工作方式与它是一个独立的正则表达式完全相同,只是它锚定在当前匹配位置并且它不消耗它匹配的内容。

Lookbehind 是一个完全不同的故事。从当前匹配位置开始,它一次一个字符地向后遍历文本,尝试在每个位置匹配其表达式。在无法匹配的情况下,后视必须一直到文本的开头(一次一个字符,记住),然后才会放弃。将其与只应用一次的前瞻表达式进行比较。

当然,这过于简单化了,并不是所有的口味都这样,但你明白了。应用lookbehinds 的方式与应用lookaheads 的方式根本不同(而且效率大大低)。只有限制后视必须向后看多远才有意义。

【讨论】:

那么你为什么不能先寻找look-behind,然后找到其余的模式呢? @AidanMueller:一些风格(包括 PCRE)支持 \K 构造,它就是这样做的:foo\Kbar 必须首先匹配 foo,但假装匹配真正开始于 bar .但这仅适用于积极后视。 @AlanMoore 这是有道理的。现在有一件事我不确定。后视是否寻找紧接在前面的内容?还是仅仅意味着“在模式之前的某个地方”? @AidanMueller:就在前面。换句话说,任何被后视匹配的东西都必须在正则表达式开始匹配的下一部分的确切位置结束。 所以从技术上讲,它可以有可变长度的lookbehind表达式,它只是不允许,因为它可能会导致性能问题?【参考方案2】:

首先,并非所有正则表达式库(如 .NET)都是如此。

对于PCRE,原因似乎是:

lookbehind的实现 断言是,对于每个备选方案, 暂时移动当前 向后定位固定宽度和 然后尝试匹配。

(至少,根据http://www.autoitscript.com/autoit3/pcrepattern.html)。

【讨论】:

为什么不对lookaheadlookbehind 使用相同的算法?原型不一样吗? wamp,那么你必须 reverse 向后看中的正则表达式并后退。正则表达式通常只能向前工作,而反转特定表达式可能很重要。 他们能够实现一个固定大小检查器(尝试#(?&lt;=fw(*SKIP)(*FAIL)|f)oo#),而从右到左的功能非常缺乏。 我认为从技术上讲,反转许多可变宽度表达式是可行的。它并不适用于所有人,但它适用于大量有用的表达,因此它是问题的部分解决方案。【参考方案3】:

PCRE 不支持浮动后视,因为它会导致严重的性能问题。这是因为缺少从右到左的匹配能力:PCRE 只能从固定的左侧开始分支,而可变长度的lookbehind 的左侧不能固定。

一般来说,如果可能,请尝试将您的后视部分分支到固定长度的模式。例如,而不是:

(?<=(src|href)=")etc.

(1) 使用这个:

(?:(?<=src=")|(?<=href="))etc.

(2) 或者\K:

(src|href)="\Ketc.

请注意,\K 不是真正的后向搜索,因为它总是在上一个匹配结束时开始搜索(没有潜在的回退到上一个匹配)。

(3) 在一些复杂的lookbehind-only 情况下,您可以在反向字符串中使用“反向”lookahead 表达式进行搜索。不太优雅,但很有效:

.cte(?="=(ferh|crs))

【讨论】:

【参考方案4】:

我遇到了同样的问题并使用(?: subexpression) 修复了它

定义一个非捕获组。比如Write(?:Line)?“WriteLine”中 "Console.WriteLine()" "Console.Write(value)" 中的 "Write"

我必须更改下面的正则表达式,它应该在, 字符串开头的某些东西给我 lookbehind assertion is not fixed length .

(?<=,|^)

有了这个,

(?:(?<=,)|^)

【讨论】:

【参考方案5】:
grep -P '(?<=((three)|(one)) )two' <<< "one two three three two one"
grep: lookbehind assertion is not fixed length

grep -P '((?<=(three) )|(?<=(one) ))two' <<< "one two three three two one"
one two three three two one

为了处理效率,PCRE 不支持从右到左的匹配或递归。当进行后向 PCRE 搜索任何先前匹配字符串的末尾时 - 实现可变大小匹配将需要递归并降低效率。见:Look Behind Assertions

【讨论】:

感谢您的回答!您能否提供一些上下文或其他信息,而不仅仅是命令?这将使答案对寻找信息的其他人更有用。

以上是关于正则表达式中“后向断言必须是固定长度”的技术原因是啥?的主要内容,如果未能解决你的问题,请参考以下文章

什么时间用正则表达式什么时间用json提取器

qt中怎么添加正则表达式使lineEdit只输入中文?

一个正则表达式酿成的惨案…

正则表达式书写规则说明

为啥正则表达式默认是贪婪的?

正则匹配引发的血案