我获得了以下关于在许多现代语言中实现正则表达式的文章的链接。
http://swtch.com/~rsc/regexp/regexp1.html
TL; DNR:某些正则表达式(例如(a?)^na^n
用于固定$ n $)将指数时间与a^n
匹配,因为它在匹配{{1}时通过回溯字符串实现} 部分。通过保留状态列表将这些实现为NFA,可以明显地提高效率
每种语言实际实现这些的详细信息并不十分详细(文章陈旧),但我很好奇:如果有的话,使用NFA与其他实现技术相比有什么缺点。我能想到的唯一的事情是,大多数库的所有花里胡哨或者a)为所有这些功能构建NFA是不切实际的,或者b)上面的表达式和其他一些表达式之间存在一些冲突的性能问题,可能更多共同的,操作。
答案 0 :(得分:6)
虽然可以很好地构建能够很好地处理这些复杂情况的DFA(由Henry Spencer编写的Tcl RE engine是一个例子证明;文章链接表明这与其性能数据)但它也是特殊的硬。
然而,一个关键的问题是,如果您可以检测到您永远不需要匹配的组信息,那么您可以(对于许多RE,尤其是没有内部反向引用的RE)将RE转换为仅使用括号进行分组的RE 允许生成更高效的RE(所以(a?){n}a{n}
- 我正在使用现代传统语法 - 变得有效等同于a{n,2n}
)。反向引用打破了主要的优化;亨利的RE代码(上面提到)中有一条代码注释将它们描述为“来自黑色泻湖的特征”。这是我在代码中读过的最好的评论之一(除了描述编码算法的学术论文的参考)。
另一方面,具有递归下降评估方案的Perl / PCRE样式引擎可以将更加理智的语义集合归于混合贪婪RE,以及许多其他内容。 (在极端情况下,递归模式 - (?R)
等 - 使用自动理论方法是完全不可能的。它们需要堆栈才能匹配,他们形式上不是正则表达式。)
在实际层面上,构建NFA的成本和随后编译的DFA可能会非常高。您需要聪明的缓存才能使它不会太昂贵。而且在实际层面上,PCRE和Perl实现已经将更多的开发人员工作应用于它们。
答案 1 :(得分:0)
我的理解是,主要原因是我们不只是对字符串是否匹配感兴趣,而是在如何与匹配,例如与捕获组。例如,(x*)x
需要知道组中有多少x,因此可以将其作为捕获组返回。类似地,它“承诺”消耗尽可能多的x个字符,这对于我们继续匹配剩余字符串的更多内容非常重要。
一些更简单的表达式可以以文章描述的有效方式匹配,而且我没有特别知道为什么不这样做。据推测,编写两个单独的引擎需要付出更多的努力,也许分析表达式以确定在其上使用哪个引擎的额外时间足够昂贵,以便在常见情况下跳过该步骤更好,并且生活在非常差的性能中。最坏的情况。
答案 2 :(得分:0)
下面:
http://haifux.org/lectures/156/PCRE-Perl_Compatible_Regular_Expression_Library.pdf
他们写道,pcre使用基于NFA的实现。但这个链接也不是网络上最年轻的东西......
在第36页附近,引擎之间有比较。它也可能与原始问题有关。