如何优化Python正则表达式的性能
介绍
Python中存在一个专门用于正则表达式的内置库,名为re。您只需导入它即可使用其功能(如搜索、匹配、查找等)。它们将返回一个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都是重复操作符的示例。当它进行部分匹配但后续失败时,正则表达式必须循环回来尝试任何其他可能的部分匹配。
以将字符串abc abc abc与正则表达式a.b.cd进行匹配为例。由于字符串中不包含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策略)。