正则表达式实际上是如何工作的?


30

假设您有一份书面论文的文件。您想分析这篇文章,仅选择某些单词。凉。

使用正则表达式是否比逐行逐单词地查找匹配项更快?如果是这样,它如何运作?您如何比查看每个单词更快?


5
您假设(隐含零证据)正则表达式会更快,但您不知道为什么会这样?那时您也许应该重新考虑您的假设。
PDR

3
因此,假设。如果我有证据,那不会是一个,对不对?
lazeR 2011年

4
那不是重点。关键是导致您做出此假设的原因……您不需要证据来证明您的问题,但是您确实需要推理出您的假设。
yannis

1
嗯,不是输入字符串的每个字符都只是将状态机移至下一个状态。我看不到有人会如何使该操作缓慢...
tp1

2
我不确定更快,但我使用正则表达式的主要原因是由于复杂匹配模式的优雅,您根本找不到在编码环境中表达它的更好方法。
Mantorok

Answers:


47

它是如何工作的?

看看自动机理论

简而言之,每个正则表达式都有一个等效的有限自动机,可以将其编译和优化为有限自动机。涉及的算法可在许多编译器书籍中找到。这些算法由awk和grep之类的unix程序使用。

但是,大多数现代编程语言(Perl,Python,Ruby,Java(和基于JVM的语言),C#)都不使用这种方法。他们使用递归回溯方法,将正则表达式编译为树或代表正则表达式各个子块的结构序列。大多数现代的“正则表达式”语法提供的反向引用不在常规语言组中(它们在有限自动机中没有表示形式),可以在递归回溯方法中轻松实现。

优化通常确实会产生更有效的状态机。例如:考虑aaaab | aaaac | aaaad,普通程序员可以在十分钟内获得简单但效率较低的搜索实现(分别比较三个字符串);但意识到它等效于aaaa [bcd],可以通过搜索前四个'a'然后针对[b,c,d]测试第5个字符来进行更好的搜索。多年前,优化过程是我的编译器家庭工作之一,因此我认为大多数现代正则表达式引擎中也是如此。

另一方面,状态机在接受字符串时确实具有一些优势,因为与“简单的实现”相比,它们使用更多的空间。考虑一个程序来取消对SQL字符串的转义,即:1)以单引号开始和结束;2)用两个连续的单引号将单引号引起来。因此:输入['a''']应该产生输出[a']。对于状态机,连续的单引号由两个状态处理。这两个状态用于记住输入历史记录,以便每个输入字符仅被处理一次,如下所示:

...
S1->'->S2
S1->*->S1, output *, * can be any other character 
S2->'->S1, output '
S2->*->END, end the current string

因此,我认为,在某些琐碎的情况下,正则表达式可能较慢,但由于人工无法可靠地进行优化,因此通常比手工制作的搜索算法要快。

(即使在诸如搜索字符串之类的琐碎情况下,智能引擎也可以识别状态图中的单个路径,并将该部分简化为简单的字符串比较,从而避免管理状态。)

框架/库中的特定引擎可能运行缓慢,因为该引擎执行了程序员通常不需要的其他许多操作。示例:.NET中的Regex类创建了一堆对象,包括Match,Groups和Captures。


2
我自己不能说的更好。我唯一要补充的是:正则表达式也可以弥补懒惰的程序员的不足。在这个例子中你提到的aaaab|aaaac|aaaadaaaa[bcd]。值得明确指出的是,两者在数学上是等效的,并且产生相同的DFA,因此使程序员有更大的自由来以有意义的方式表示正则表达式(不是这是通常的做法,而是...知道)。 ..
riwalk

谢谢,这实际上要归功于我参加的自动机课程
lazeR 2011年

这是一个很重要的问题的例子,其中正则表达式是矫枉过正?:stackoverflow.com/questions/18955099/...
墨涅拉俄斯Bakopoulos

17

正则表达式看起来很快,因为您拥有快速的计算机。

早在1980年代1 MIPS是一台快速计算机时,正则表达式是一个相当大的担忧,关注和研究领域,因为它们速度慢,丑陋且计算量大。随之而来的是巧妙的算法开发并为之提供了帮助-但如今,出于所有实际目的,您已经看到了快速机器在裂缝上铺开的奇迹。


2
如果您只想查找一个单词,则这两种方法都相同(或regexp稍慢一些)。但是给定一个复杂的表达式(以及相当大的文本),则正则表达式可能比简单的搜索要快(假设您简单地编写简单的搜索(您总是可以编写同样快的复杂搜索))。现在,重要的天气问题太笼统了,您必须根据具体情况进行研究。
马丁·约克

3
-1。正则表达式的理论可以追溯到50年代,并且在创建词法分析器(并扩展为编译器)方面发挥了作用。他们创建了非常有效的状态机,状态机(证明)使用了尽可能少的状态。生成的状态机可以比您手动编写的任何东西更快地匹配复杂的模式。它们看起来很快,因为它们很快。
riwalk

可能会遗漏我的观点。它们可能是“快速的”,但是那都是相对的-仍有大量工作要做。这里的其他一些答案也需要阅读。
quick_now 2011年

这个答案和这个问题有关吗?以及13个投票如何?
Sadanand

7

您为什么认为它们比搜索文档更快?

您可以采取一些技巧,例如。如果您要搜索一个以A开头并以B结尾的10个字母的单词,那么如果您找到A且后面第9个字符不是B,则可以跳过一些。请参阅Knuth–Morris–Pratt算法


5

什么使正则表达式快速?

实际上,事实并非如此。没有那么多。只是它们的速度不足以让我们大多数人注意到。回到过去的缓慢日子,它更加引人注目。

它们并不是每项工作的正确工具-锤子


+1感谢您让我想起那件特殊的艺术品……
yannis

5

正则表达式的编写速度相对而言要快一些,因为大多数库是许多开发人员花费多年时间对其进行优化以消除可能的所有性能的结果。一个人很难在自己的搜索代码中重复该代码。


4
s /吱吱声/挤压/?
彼得Török

4

您的基本前提是错误的。

正则表达式并不总是比简单搜索快。这完全取决于上下文。它取决于表达式的复杂性,要​​搜索的文档的长度以及许多因素。

发生的事情是正则表达式将被编译成一个简单的解析器(这需要时间)。因此,如果文档很小,那么额外的时间将超过任何好处。同样,如果表达式很简单,则正则表达式将不会给您带来任何好处。

如果表达式很复杂并且文档足够大,那么您可以获得一些好处。这是否足以使正则表达式考虑得更快,将在很大程度上取决于您要在搜索中付出多少努力(而且正则表达式可能会进行一些优化,而图书馆可能会提供一些优化,而您可能不会想到自己)。

我要说的是,没有广义的,笼统的答案。如果您有一个特定的表达式(和已知的文档大小),则可以说是/否答案,该表达式是否比简单的搜索更快(以及为什么)。

正则表达式的真正优势在于,一旦您了解如何编写它们,便能够以简洁的方式表达复杂的搜索。因为它是通用形式,所以您可以构建工具,以一种在一般情况下有用的方式进行搜索。它通常至少与简单搜索一样快(在最小尺寸的文档上;在小于此的文档上,这没有关系,因为即使速度较慢,它仍然足够快)。


1

在某些高级语言(可能是javascript)中,使用以低级语言(也许是C)实现的正则表达式库比用高级语言编写解析器逻辑要快。

可能-我不知道是否真的是这样。


好东西!我也是考虑过这一点的。但是,由于当今的处理器比以前的处理器快,我可以肯定地说,如果您高效地编写代码,您将几乎无法分辨。实际上,我总体上并不是对整个正则表达式更快的假说提出质疑!;-)
user3833732
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.