为什么Javascript不支持lookbehind断言?

时间:2015-05-08 08:03:28

标签: javascript regex

最近,我意识到(一些尴尬lookbehind assertions中正则表达式Javascript 不可能

这种断言缺席的(事实)原因是什么似乎很常见?

I realize there are alternate ways to achieve the same thing perhaps,虽然这是工作中禁止功能的基本语义,还是究竟是什么?

似乎那些从正则表达式模式生成Javascript代码的regex testing tools似乎忽略了这一事实 - 这让我觉得有些奇怪。

2 个答案:

答案 0 :(得分:26)

今天

Lookbehind现在是ES 2018 specification的官方部分。 Axel Rauschmayer给出good introduction in his blog post

历史

It looks like at the time, Brendan Eich wasn't aware of its existence(因为Netscape是基于旧版本的Perl构建的):

  

这是1998年,我在#9; 97中做过的Netscape 4工作是基于Perl 4(!),但是我们向ECMA TC39 TG1提出了建议(JS组 - 当时的事情不同,包括大写)在Perl 5.我们没有得到所有东西,我们不得不合理化一些明显的怪癖。

     

我不记得lookbehind(在7月的Perl 5.005和#39; 98中出现)被故意排除在外。 Waldemar可能还记得更多,我已经把他在netscape.com里面的JS密钥递给了mozilla.org。

     

如果你是写游戏或迷你规格的游戏(甚至是ES5的风格),请告诉我。我将在下周与其他TC39的人聊聊此事。

     

/是

邮件列表上有许多不同的尝试包含它,但它似乎仍然是性能相当复杂的功能,因为EcmaScript Regular Expressions基于backtracking并且回溯是在使用捕获组时需要后视镜。如果使用不当,可能会导致catastrophic backtracking等问题。

在某些时候,它被建议用于ES6 / Es 2015,但它从未制定过草案,更不用说规范了。在last post in the discussion中,似乎没有人承担实施它的任务。如果有人感觉被要求编写实现,他们可以注册ES Discuss list并提出建议。

2015年5月更新:

2015年5月,Nozomu Katō has proposed an ES7 look-behind implementation

2015年9月更新:

Regex Look-behind被添加为stage 0 proposal

2017年5月更新:

The proposal is now at stage 3。这意味着现在至少有两个浏览器需要实现它才能成为下一个EcmaScript标准的一部分。正如@martixy在评论中提到的那样Chrome has implemented it behind the JS experimental flag

答案 1 :(得分:7)

从结论来看,我认为在JavaScript中没有实现后视,因为没有人知道应该的行为如何,现有的实现表明添加对后视镜头的支持是相当复杂。

JavaScript / ECMAScript与其他语言的不同之处在于规范包含了正则表达式引擎的抽象实现,而大多数其他语言只是在描述每个正则表达式语法的行为方面停顿不前,对于如何区别的描述很少令牌相互作用。

前瞻?易于实施

预见的实施非常简单。您只需要以与前瞻模式相同的方式处理前瞻中的模式,并按照惯例执行从左到右的匹配,除了在前瞻成功之后1)当前位置是在进入前瞻之前恢复到,并且2)预测中的选择点在匹配后被丢弃。

对于可以包含在内部预测中的内容没有限制,因为它是对现有自然左右匹配设施的非常简单的扩展。

看隐藏?不那么容易

另一方面,后视的实施并不是那么简单。

想象一下如何实现以下后视构造:

(?<=fixed-string)
(?<=a|fixed|string)
(?<=t[abc]{1,3})
(?<=(abc){2,6})
(?<=^.*abc.*)
(?<=\G"[^"]+");
(?<=^(.....|.......)+)
\b(\w+)\b(?<!\b\1\b.*\1)

除了基本案例(?<=fixed-string)之外,任何后备实现都必须支持,(?<=a|fixed|string)是一个非常理想的支持案例。

不同的正则表达式引擎对上面的正则表达式有不同程度的支持。

让我们看看它们是如何在.NET和Java中实现的。 (这是我研究过的两种风格。)

.NET实现

在Microsoft .NET实现中,上面的所有正则表达式都是有效的,因为.NET通过使用从右到左模式实现后视,并在当前位置使用起始偏移量。后视构造本身不会产生任何选择点。

但是,如果在后视中使用捕获组,则会开始变得混乱,因为模式中的原子是从右到左解释的as demonstrated in this post。这是这种方法的缺点:在编写一个后视时,你需要把思路从右到左思考。

Java实现

相比之下,Java正则表达式实现通过重用从左到右的匹配工具来实现后视。

首先分析后视图案中的模式的最小和最大长度。然后,通过尝试从左到右匹配内部模式来实现后视,从(current position - minimum length)(current position - maximum length)

有什么遗漏?是!由于我们从左到右进行匹配,因此我们需要确保匹配在进入后视(current position)之前的位置结束。在Java中,这是通过在模式的末尾追加一个节点来实现的。

此实现非常低效,因为在我们甚至讨论由后视图中的模式创建的选择点之前,在后视本身中创建了maximum - minimum + 1个选择点

后视界限检查效率也很低,因为它位于模式的末尾,无法修剪明显无望的选择点(那些已经远远超过current position的选择点图案)。

摘要

正如您所看到的,添加对后视镜头的支持并不容易:

  • 从右到左的方法似乎相当有效。但是,它需要对其他现有构造的从右到左匹配行为进行额外规范。
  • 重复使用从左到右匹配工具的方法很复杂,而且效率很低。它还需要在规范中引入模式分析,以免性能被抛到窗外。

(请注意,我在前面使用look-behind时尚未涵盖这种行为,反之亦然。在为后置构造定义语义时也应该考虑这一点。)

the mail引用的nils' answer中的Waldemar Horwat(编写ES3正则表达式规范)也提到了这些技术障碍:

  

目前还没有人提交明确定义的关于表格背后的提议。 Lookbehinds难以转化为规范所使用的语言,并且当正则表达式的部分评估顺序很重要时会变得非常模糊,如果涉及捕获括号则会发生这种情况。你从哪里开始寻找外观?最短的第一个,最长的第一个或反向字符串匹配?贪婪与否?回溯到捕获结果?