哪个正则表达式表现更好?

时间:2014-02-06 07:20:52

标签: regex

我希望匹配具有公共前缀(/ files)的URL,但只有两个特定目录(图像和视频)。我可以想到两个正则表达式:

/files/(images/.*|videos/.*)

/files/(image|video)s/.*

我在这里有两个问题:

  1. 从表现的角度来看,哪一个更好?我的猜测是第二个,因为它的DFA将拥有较少数量的州。
  2. 是否有通用编程语言,其内置的正则表达式编译器会将给定的正则表达式减少为最小DFA?
  3. 性能对我很重要,因为我将使用它来匹配数十亿字符串。所以,任何微小的改进对我都很重要。

3 个答案:

答案 0 :(得分:2)

  

从性能角度来看哪一个更好?我的猜测是第二个,因为它的DFA将拥有较少数量的州。

两个表达式在最小DFA中具有相同数量的状态,并且它们的DFA匹配相同的“语言”(在理论意义上)。

无论DFA中的状态数量如何,性能在理论上都是相同的,因为在将输入提供给自动机时,您将确定性地遍历各个状态。

在实践中,由于缓存未命中可能存在差异,当存在更多状态时可能更频繁地发生。但是,除非您正在使用正则表达式引擎,否则我无法想出花时间进行黑盒优化的任何理由。

  

是否有通用编程语言,其内置的正则表达式编译器会将给定的正则表达式减少为最小DFA?

Go(re2)和Haskell具有将正则表达式转换为DFA的引擎。不过,我不知道DFA是否很小。

由于POSIX ERE不支持反向引用(反向引用不同于引用替换中的捕获组),因此可以为在DFA上运行的POSIX ERE编写引擎或有效的NFA模拟。但是,由于标准不会强制执行此类实现,只要结果正确(匹配最左边最长的字符串),实现就可以穷尽地搜索与NFA回溯引擎上的正则表达式匹配的所有字符串。

但是,至少GNU egrep seems to implements POSIX ERE with DFA(基于文件名dfa.c)。


有关您的信息,请3 approaches to implement a regular expression matching

  • DFA
  • NFA仿真算法
  • NFA回溯

有关详细信息,请articlethis question中引用)解释:

  • 对于(理论)正则表达式,存在具有子匹配跟踪(捕获组)的高效NFA模拟算法。
  • 为什么回溯引擎如此突出(例如Java,Python,Perl 2 ,......)
    2 :Perl使用memoization实现回溯引擎。
  • 回溯引擎可能在输入长度上占用指数时间,而Thompson的NFA模拟算法需要O(mn)时间,其中m是正则表达式的长度,n是输入的长度。
  • 匹配(ir)正则表达式与目前已知的反向引用的最有效算法是回溯方法。因此,一些引擎决定不支持反向引用以提高匹配效率。
  • 回溯引擎(即使有记忆)比Thompson的NFA模拟算法慢。

顺便说一下,re2引擎(如上所述)包括基于DFA和基于NFA(高效模拟)匹配算法的实现。

答案 1 :(得分:0)

Python 2.7说:

import timeit
once = 'import re; m="/files/images/test"'
num = 1000000
print timeit.timeit(stmt='re.findall(r"/files/(images/.*|videos/.*)", m)', setup=once, number=num)
-> 1.5884420871734619
print timeit.timeit(stmt='re.findall(r"/files/(image|video)s/.*", m)', setup=once, number=num)
-> 1.5990869998931885

这使用正则表达式100万次,并且在多次运行两行之后都具有相同的速度。

Python可能会缓存已编译的正则表达式...

我用

测试了它
  • /文件/图像/测试
  • /文件/视频/测试
  • /文件/ viddeos /测试

你的第一个版本(/files/(images/.*|videos/.*))在我的测试中运行了一点(0.1秒)

答案 2 :(得分:0)

我的直觉(不太有帮助,我知道)会说第二种选择是更好的选择。但请考虑一下:

  • 您为什么要添加.*
  • 您可以将正则表达式锚定到该行的开头吗?
  • 您是否需要捕获该组?

如果适用,我会这样做:

^/files/(?:image|video)s/