正则表达式中“lookbehind断言必须固定长度”的技术原因是什么?

时间:2010-09-26 02:59:54

标签: regex pcre lookbehind

例如,下面的正则表达式将导致失败报告 lookbehind断言不是固定长度

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

lookahead不存在此类限制。

5 个答案:

答案 0 :(得分:63)

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

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

当然,这是一个粗略的过度简化,并不是所有的口味都是这样,但你明白了。应用lookbehinds的方式与前瞻应用方式的根本不同(并且很多,很多效率低)。限制后视镜的外观是有意义的。

答案 1 :(得分:9)

首先,对于所有正则表达式库(如.NET),情况并非如此。

对于PCRE,原因似乎是:

  

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

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

答案 2 :(得分:5)

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

通常,如果可能,尝试将lookbehind部分分支到固定长度模式。例如,而不是:

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

(1)使用此:

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

(2)\K

(src|href)="\Ketc.

请注意\K不是真正的后视,因为它始终在上一场比赛结束时开始搜索(没有潜在的后退到前一场比赛)。

(3)在一些复杂的仅后视案例中,您可以使用反向字符串中的“倒置”前瞻表达式进行搜索。不太优雅,但它有效:

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

答案 3 :(得分:2)

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

修复了该问题
  

定义非捕获组。例如Write(?:Line)?&#34; WriteLine&#34;在   &#34; Console.WriteLine()&#34; &#34;写&#34;在&#34; Console.Write(值)&#34;

我必须更改下面的正则表达式,假设在, 之前捕获字符串开头的东西,这给了我 lookbehind断言不是固定长度

(?<=,|^)

有了这个,

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

答案 4 :(得分:0)

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