我希望匹配具有公共前缀(/ files)的URL,但只有两个特定目录(图像和视频)。我可以想到两个正则表达式:
/files/(images/.*|videos/.*)
和
/files/(image|video)s/.*
我在这里有两个问题:
性能对我很重要,因为我将使用它来匹配数十亿字符串。所以,任何微小的改进对我都很重要。
答案 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:
有关详细信息,请article(this question中引用)解释:
顺便说一下,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可能会缓存已编译的正则表达式...
我用
测试了它你的第一个版本(/files/(images/.*|videos/.*)
)在我的测试中运行了一点(0.1秒)
答案 2 :(得分:0)
我的直觉(不太有帮助,我知道)会说第二种选择是更好的选择。但请考虑一下:
.*
?如果适用,我会这样做:
^/files/(?:image|video)s/