最近我(通过一些尴尬)意识到正则表达式lookbehind assertions
是不可能的Javascript
.
这种断言缺席的(事实)理由是什么似乎很常见?
我意识到也许有其他方法可以实现相同的功能,虽然它是工作中禁止功能的基本语义,还是究竟是什么?
似乎一些从正则表达式模式生成Javascript代码的正则表达式测试工具似乎忽略了这个事实 - 这让我觉得有些奇怪.
nil*_*ils 29
Lookbehind现在是ES 2018规范的官方部分.Axel Rauschmayer 在他的博客文章中给出了一个很好的介绍.
看起来当时,Brendan Eich并不知道它的存在(因为Netscape是基于旧版本的Perl构建的):
这是1998年,我在97年做的Netscape 4工作是基于Perl 4(!),但是我们提议ECMA TC39 TG1(JS组 - 事情不同,包括大写)基于Perl 5的东西.我们没有得到一切,我们不得不理顺一些明显的怪癖.
我不记得lookbehind(在1998年7月出现在Perl 5.005中)被故意排除在外.Waldemar可能还记得更多,我在netscape.com里面把他的JS密钥递给了mozilla.org.
如果你是写游戏或迷你规格的游戏(甚至是ES5的风格),请告诉我.我将在下周与其他TC39的人聊聊这件事.
/是
在邮件列表上有许多不同的尝试包含它,但它似乎仍然是性能相当复杂的功能,因为EcmaScript正则表达式是基于回溯的,并且在使用捕获组时需要回溯.这可能导致诸如使用不当时的灾难性回溯等问题.
在某些时候,它被建议用于ES6/Es 2015,但它从未制定过草案,更不用说规范了.在讨论的最后一篇文章中,似乎没有人承担实施它的任务.如果有人感觉被要求编写实现,他们可以注册ES讨论列表并提出建议.
2015年5月,NozomuKatō提出了ES7后视实施方案.
Regex Look-behind被添加为0阶段提案.
该提案现在处于第3阶段.这意味着现在至少有两个浏览器需要实现它才能成为下一个EcmaScript标准的一部分.正如@martixy在评论中提到的,Chrome已经在JS实验标志后面实现了它.
从结论来看,我认为在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)
Run Code Online (Sandbox Code Playgroud)
除了(?<=fixed-string)
任何后备实现必须支持的基本情况之外,(?<=a|fixed|string)
还有一个非常理想的支持案例.
不同的正则表达式引擎对上面的正则表达式有不同程度的支持.
让我们看看它们是如何在.NET和Java中实现的.(这是我研究过的两种风格.)
在Microsoft .NET实现中,上面的所有正则表达式都是有效的,因为.NET通过使用从右到左模式实现后视,并在当前位置使用起始偏移量.后视构造本身不会产生任何选择点.
但是,如果在后视中使用捕获组,则会开始变得混乱,因为模式中的原子是从右到左解释的,如本文所示.这是这种方法的缺点:在编写一个后视时,你需要把思路从右到左思考.
相比之下,Java正则表达式实现通过重用从左到右的匹配工具来实现后视.
它首先分析了后视内部的模式,了解模式的最小和最大长度.然后,通过尝试从左到右匹配内部模式来实现后视,从开始(current position - minimum length)
到(current position - maximum length)
.
有什么遗漏?是! 由于我们从左到右匹配,我们需要确保匹配在进入后视(current position
)之前的位置结束.在Java中,这是通过在模式的末尾附加一个节点来实现的.
这种实现效率非常低,因为maximum - minimum + 1
在我们甚至讨论由后视图中的模式创建的选择点之前,在后视本身中创建了选择点.
后视边界检查也是低效的,因为它被放置在模式的末尾,并且不能修剪明显无望的选择点(那些已经远远超过current position
模式中间的选择点).
如您所见,添加对后视镜头的支持并不容易:
(注意,当在内部预测中使用后视时我还没有涵盖这种行为,反之亦然.在为后置构造定义语义时也应该考虑这一点).
在nils回答中引用的邮件中,Waldemar Horwat(编写ES3正则表达式规范)也提到了这些技术障碍:
目前还没有人提出明确定义的关于外观的提案.Lookbehinds难以转化为规范使用的语言,并且当正则表达式的部分评估顺序很重要时会变得非常模糊,如果涉及捕获括号则会发生这种情况.你从哪里开始寻找外观?最短的第一个,最长的第一个或反向字符串匹配?贪婪与否?回溯到捕获结果?