【问题标题】:Python Regex slower than expectedPython 正则表达式比预期慢
【发布时间】:2015-09-18 19:15:05
【问题描述】:

我读了一篇很酷的article,关于如何避免创建缓慢的正则表达式。一般来说,它看起来越长越明确,而正则表达式完成的速度越快。贪婪的正则表达式可能会以指数方式变慢。

我想我会通过测量用不太复杂/贪婪的语句完成更复杂/明确的语句所需的时间来测试这一点。在大多数情况下,一切似乎都是正确的,但我有一个贪婪的陈述,速度变慢了。这里有两个例子:

import re
from timeit import timeit

# This works as expected, the explicit is faster than the greedy.
# http_x_real_ip explicit 
print(timeit(setup="import re", stmt='''r = re.search(r'(\d{1,3}\.\d{1,3}.\d{1,3}.\d{1,3})', '192.168.1.1 999.999.999.999')''', number=1000000))
1.159849308001867

# http_x_real_ip greedy
print(timeit(setup="import re", stmt='''r = re.search(r'((?:\d{1,3}\.){3}\d{1,3})', '192.168.1.1 999.999.999.999')''', number=1000000))
1.7421739230003368

# This does not work as expected, greedy is faster.
# time_local explicit
print(timeit(setup="import re", stmt='''r = re.search(r'(\d{1,2}/\w{3}/[2][0]\d{2}:\d{2}:\d{2}:\d{2}\s[+][0]{4})', "[23/Jun/2015:11:10:57 +0000]")''', number=1000000))
1.248802040994633

# time_local greedy
print(timeit(setup="import re", stmt='''r = re.search(r'\[(.*)\]', "[23/Jun/2015:11:10:57 +0000]")''', number=1000000))
1.0256699790043058

local_time 显式正则表达式是否写得不好?

【问题讨论】:

  • time_local 贪心是不等价的。它只匹配 [] 之间的任何内容。
  • 也许 time_local 贪婪的执行速度比预期的要快?
  • time_local greedy 的执行速度比预期的要快......也许如果被解析的字符串相当长,例如完整的日志条目,由于额外的回溯,它会花费更长的时间?我得试试看。
  • 您的最后一个模式更快,因为正则表达式引擎需要考虑的标记很少,而且只需一个回溯步骤即可使模式成功。
  • 请注意,第一个模式是错误的,因为您忘记了转义点。

标签: python regex performance


【解决方案1】:

正则表达式回溯越多,越慢。

这可能不适用于非常小的输入数据。但是,谁会关心小数据的性能呢? :D


这篇文章很好地涵盖了这个主题:

这个问题也有有趣的贡献:

【讨论】:

    【解决方案2】:

    您也没有使用 Python 正则表达式的 re.compile 功能,这意味着您的搜索时间还包括 re 模块在每次迭代时编译正则表达式的时间。

    >>> print(timeit(setup="import re", stmt='''r = re.search(r'(\d{1,3}\.\d{1,3}.\d{1,3}.\d{1,3})', '192.168.1.1 999.999.999.999')''', number=1000000))
    0.73820400238
    >>> print(timeit(setup="import re; regex = re.compile(r'(\d{1,3}\.\d{1,3}.\d{1,3}.\d{1,3})')", stmt='''r = regex.search('192.168.1.1 999.999.999.999')''', number=1000000))
    0.271140813828
    >>> print(timeit(setup="import re; regex = re.compile(r'((?:\d{1,3}\.){3}\d{1,3})')", stmt='''r = regex.search('192.168.1.1 999.999.999.999')''', number=1000000))
    0.31952214241
    >>> print(timeit(setup="import re; regex = re.compile(r'(\d{1,2}/\w{3}/[2][0]\d{2}:\d{2}:\d{2}:\d{2}\s[+][0]{4})')", stmt='''r = regex.search("[23/Jun/2015:11:10:57 +0000]")''', number=1000000))
    0.371844053268
    >>> 
    

    这里的贪婪和非贪婪正则表达式之间的区别实际上更接近预编译时的预期。其余的解释都是回溯。

    如果您为大量迭代预编译正则表达式,我们可以看到您的测试速度几乎提高了 3 倍。

    此答案旨在补充@mescalinum 的答案,但对于大量正则表达式,您确实应该提前编译正则表达式以进行公平比较。

    【讨论】:

    • 太棒了!感谢您的分享,re.compile 看起来非常有用。
    猜你喜欢
    • 1970-01-01
    • 2012-12-21
    • 2012-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 2018-05-08
    相关资源
    最近更新 更多