两个数量级的性能变化与貌似很小的python regex chang

2024-06-28 20:43:06 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个很慢的python正则表达式,如果我只删除regex的最后一行,速度会提高两个数量级!下面是我的复制示例:

import re
import timeit

mystr = "14923KMLI MLI2010010206581258   0.109 b                              M     M     M     0    09 60+              "

basere = r"""
(?P<wban>[0-9]{5})
(?P<faaid>[0-9A-Z]{4})\s
(?P<id3>[0-9A-Z]{3})
(?P<tstamp>[0-9]{16})\s+
\[?\s*((?P<vis1_coef>\-?\d+\.\d*)|(?P<vis1_coef_miss>M))\s*\]?\s*
\[?(?P<vis1_nd>[0-9A-Za-z\?\$/ ])\]?\s+
((?P<vis2_coef>\d+\.\d*)|(?P<vis2_coef_miss>[M ]))\s+(?P<vis2_nd>[A-Za-z\?\$ ])\s+
...............\s+
\[?\s*((?P<drct>\d+)|(?P<drct_miss>M))\s+
((?P<sknt>\d+)|(?P<sknt_miss>M))\s+
((?P<gust_drct>\d+)\+?|(?P<gust_drct_miss>M))\s*\]?\s+
"""
additional = r"""
\[?((?P<gust_sknt>\d+)R?L?F*\d*\+?|(?P<gust_sknt_miss>M))\s*\]?\s+
"""

P1_RE = re.compile(basere + additional, re.VERBOSE)
P2_RE = re.compile(basere, re.VERBOSE)


for myre in ["P1_RE", "P2_RE"]:
    statement = "%s.match('%s')" % (myre, mystr)
    res = timeit.timeit(statement, "from __main__ import %s" % (myre,), 
                        number=1000)
    print('%s took %.9f per iteration' % (myre, res / 1000.))

# result on my laptop, python 2.6 and 3.3 tested
# P1_RE took 0.001489143 per iteration
# P2_RE took 0.000019991 per iteration

因此P1_RE和{}之间的唯一区别是附加的regex。你知道我做错了什么吗?在


Tags: importrep2p1timeitmissvis1coef
1条回答
网友
1楼 · 发布于 2024-06-28 20:43:06

这是因为回溯;这是正则表达式效率低下的一个突出原因。在

删除的最后一行要求至少匹配1个数字或M,后面跟着空格,以及许多很多可选的东西。在

倒数第二行:

((?P<gust_drct>\d+)\+?|(?P<gust_drct_miss>M))\s*\]?\s+

最初匹配最后一部分,即60+和后面的空格,最后一行不留任何内容。因此,正则表达式无法匹配,并一次回溯一个字符以尝试匹配最后一行。问题是,正则表达式将尝试匹配很多内容,对于正则表达式回溯的每个字符,都有越来越多的尝试匹配。在

在正则表达式匹配之前,它实际上已经拒绝了很多之前正则表达式行匹配过的字符。在


一个简单的例子是字符串(10个A):

^{pr2}$

以及正则表达式:

a+a+a+a+a+a+a+a+a+a+

regex的第一部分a+将匹配整个字符串(因为+是贪婪的),但是接下来,它需要匹配更多的{},因为regex的下一部分是a+。然后引擎回溯一个字符,使第一个a+匹配aaaaaaaaa(9a),第二个匹配最后一个a。在

下一个是第三个a+,由于没有更多的a匹配,regex将回溯一个字符。由于第二个a无法回溯,因此第一个a+aaaaaaaa(8a)匹配,第二个{}将匹配最后一个{}。现在没有了a,所以第二个a+将回溯以匹配倒数第二个a,最后,第三个a+可以匹配最后一个a。在

看看前3a+做了多少?现在想象一下,通过每一个可能的匹配,直到所有匹配?在


通常,如果您根本不想回溯,您可以使用原子组和所有格量词,但python不支持这些,至少,re模块不支持这些功能(^{}模块支持)。在

您可能需要修改regex并尝试查看它应该真正匹配什么,什么是真正可选的,尤其是捕获组中可能的空格。在

相关问题 更多 >