如何优化 Python 正则表达式的性能?
简介
在 Python 中,存在一个名为 re 的特定于 正则表达式 的内置库。您只需要导入它即可使用其功能(例如搜索、匹配、findall 等)。它们将为您返回一个 Match 对象,其中包含用于修改结果的有用技术。
根据维基百科,正则表达式(也称为 regexp)是指定搜索模式的字符集合。它是一种工具,使您能够过滤、提取或更改一系列字符。还发现,当使用“in”运算符时,正则表达式运行速度更快。
正则表达式存在性能问题,并且通常难以调试和维护。为了提高其性能,必须解决这些问题。
示例
import re my_string = 'I like tortilla.' # 'like (\w+)' is a regexp that matches 'like' followed by a space followed by # any number of word characters ('\w' means 'word character' and '+' means '1 or more'), # putting those word characters in a capture group (the parenthesis) regex_result = re.search(r'like (\w+)', my_string) print(regex_result.groups())
输出
('tortilla',)
Soundex 函数首先检查输入是否为非空字母字符串。最好的方法是什么?
如果您回答“正则表达式”,请坐在角落里反思您糟糕的直觉。正则表达式很少是正确的答案;应尽可能避免使用它们。不仅出于性能原因,而且仅仅因为它们难以调试和维护。此外,也出于性能原因。
这段来自 soundex/stage1/soundex1a.py 的代码片段检查函数参数 source 是否完全由字母组成的单词,并且至少包含一个字母(非空字符串) -
为什么正则表达式效率很重要?
设计不当的正则表达式可能需要很长时间才能执行,并严重降低系统速度,即使精心设计的正则表达式可能非常有效。BMC Discovery 经历了多次升级,使其比早期版本更能抵抗无效的正则表达式。
当应用于中等大小的字符串时,设计一个需要花费数小时、数天甚至整个宇宙生命周期才能完成的正则表达式是完全可以想象的。此外,它将执行 TPL 模式的工作分散到多个处理器上,以便即使一个处理器忙于长时间的正则表达式匹配,其他处理器也可以继续工作。
低效正则表达式的剖析
那么,如何创建一个低效的常用短语呢?一个问题是正则表达式回溯得太远;如果正则表达式有多个重复运算符,则可能会发生这种情况。+, * 或 n, m 是重复运算符的示例。如果正则表达式进行部分匹配但在稍后失败,则必须循环回并尝试任何其他可能的局部匹配,以防其中任何一个成功。
以使用正则表达式 a.*b.*cd 匹配字符串 abc abc abc 为例。由于字符串中不包含 d,因此匹配将永远不会成功。但是,在放弃之前,正则表达式仍然必须穷尽字母组合 a、b 和 c 的所有可能性。
"*abc* abc abc", "*ab*c ab*c* abc", "*ab*c abc ab*c*", "*a*bc a*bc* abc", "*a*bc a*b*c ab*c*", "*a*bc abc a*bc*", "abc *abc* abc", "abc *ab*c ab*c*", "abc *a*bc a*bc*", "abc abc *abc*"
作为一个粗略的指南,正则表达式需要执行的比较次数与字符串长度乘以可能的中间匹配数成正比。
在这个使用非贪婪运算符的示例中,即 a.*?b.*?cd,对它将进行的匹配次数没有影响,因为正则表达式引擎仍然需要尝试每种组合。>
编写高效正则表达式的指南
考虑潜在的失败情况
正如前面的示例所示,当正则表达式完全无法匹配时,但存在多个部分匹配时,问题就会出现。在编写正则表达式时,考虑正则表达式在失败时如何运行以及在成功时会发生什么非常重要。
尝试快速失败
如果正则表达式到达无法匹配所需目标的点,请尝试使整个正则表达式失败。
分析 - 特别是失败的案例
为了确保您的正则表达式匹配您预期的内容,验证它至关重要。但是,针对仅部分匹配的长字符串(例如兆字节长的随机字母字符串)评估正则表达式的效率也很重要。
除非必要,否则不要使用组
当您使用括号括住正则表达式的某一部分时,正则表达式引擎必须更加努力地保留由该组匹配的文本,以防以后需要它。这可能会导致匹配过程变慢,有时会慢四倍或更多。
如果您需要使用括号但不需要使用组的内容,例如当正则表达式的某一部分重复时,可以使用括号的非分组变体(?:)。
结论
有人可能会反驳说 Pandas 对于此类操作更胜一筹。但是,我不认为它会像我们构建的纯 Python 版本那样快,因为它需要更长的时间才能将数据集加载到 DataFrame 中。
其他选项,例如使用 regex 库或将数据分成多个部分并并行计数,可以进一步提高速度(一种与 map-reduce 相关的策略,这是 大数据 中非常相关的算法)。