防止RegEx在大型比赛中挂起

时间:2009-12-18 19:11:04

标签: python regex

这是日期的一个很好的正则表达式...但是它在我试过的这一页上无限期挂起...我想尝试这个页面(http://pleac.sourceforge.net/pleac_python/datesandtimes.html)因为它确实有很多日期在它上面,我想抓住所有这些。我不明白为什么它在其他页面上没有悬挂...为什么我的正则表达式挂起和/或我怎么能清理它以使它更好/更有效?

Python代码:

monthnames = "(?:Jan\w*|Feb\w*|Mar\w*|Apr\w*|May|Jun\w?|Jul\w?|Aug\w*|Sep\w*|Oct\w*|Nov(?:ember)?|Dec\w*)"

pattern1 = re.compile(r"(\d{1,4}[\/\\\-]+\d{1,2}[\/\\\-]+\d{2,4})")

pattern4 = re.compile(r"(?:[\d]*[\,\.\ \-]+)*%s(?:[\,\.\ \-]+[\d]+[stndrh]*)+[:\d]*[\ ]?(PM)?(AM)?([\ \-\+\d]{4,7}|[UTCESTGMT\ ]{2,4})*"%monthnames, re.I)

patterns = [pattern4, pattern1]

for pattern in patterns:
    print re.findall(pattern, s)

btw ...当我说我试图反对这个网站..我正在尝试它反对网页来源。

4 个答案:

答案 0 :(得分:5)

您应该阅读Mastering Regular Expressions。问题是:

(?:[\d]*[\,\.\ \-]+)*

需要指数时间。尝试使用:

(?:[\d,. \-]*[,. \-])?

哪个应该匹配相同的东西,但需要线性时间。检查了你的例子,这确实加快了速度。

您似乎也偶然在某个时刻将捕获组引入了您的模式:例如,更改(AM)到(?:AM)来解决这个问题。这将从上面的示例中获取以下输出:

[' Aug  6 20:43:20 2003', ' Mar 14 06:02:55 1973', ' March 14 06:02:55 AM 1973', ' Jun 16 20:18:03 1981']
['2003-08-06', '2003-08-07', '2003-07-23', '1973-01-18', '3/14/1973', '16/6/1981', '16/6/1981', '16/6/1981', '16/6/1981', '08/08/2003']

要深入了解详细信息(我参考的那本书非常擅长),*和+工作(在NFAs中就像python一样),就像一个循环。原始模式的内部循环将匹配一长串数字,但是当后续模式无法匹配时,它将一次“放弃”一个。然后外循环将重新运行剩余模式的内循环,当然它会立即再次抓取数字。每次内循环的一个实例放弃一个数字时,将召唤一个新副本再次抓取它。最终,一旦引擎完成了分割该数字串(指数的可能性)的每种可能方式,它将向前移动一个字符......然后重试。

另一方面,你的模式看起来有点疯狂;)

答案 1 :(得分:0)

Python正则表达式求值程序可能需要很长时间。不幸的是,它在最坏的情况下以指数时间运行。

我认为你的“s”包含整个页面的副本。如果是这样,那么可能会导致正则表达式计算器中出现很长的回溯。也许您应该将页面分成更小的块,并通过正则表达式单独运行它们。您可以使用像beautifulsoup这样的HTML解析器,并分别在每个文本节点上运行正则表达式。这可能会缩短运行时间。

答案 2 :(得分:0)

正则表达式的编写方式将导致批次的回溯。除了关于在较小的文本块上运行它的提示之外,您还可以使用更简单(并且因此更快)的正则表达式来过滤掉无法匹配的文本。

答案 3 :(得分:0)

首先,你应该读一下r""字符串的意思:你只需要在反斜杠中放入你真正需要反斜杠的东西,所以你的正则表达式应该只是:

monthnames = "(?:Jan\w*|Feb\w*|Mar\w*|Apr\w*|May|Jun\w?|Jul\w?|Aug\w*|Sep\w*|Oct\w*|Nov(?:ember)?|Dec\w*)"

pattern1 = re.compile(r"(\d{1,4}[-/]+\d{1,2}[-/]+\d{2,4})")

pattern4 = re.compile(r"(?:\d*[,. -]+)*%s(?:[,. -]+\d+[stndrh]*)+[:\d]*[ ]?(PM)?(AM)?([ -+\d]{4,7}|[UTCESTGMT ]{2,4})*"%monthnames, re.I)

关于你的真正问题,Python在嵌套在*内的*表现不佳。将pattern4更改为此(第一个\d*变为\d+):

pattern4 = re.compile(r"(?:\d+[,. -]+)*%s(?:[,. -]+\d+[stndrh]*)+[:\d]*[ ]?(PM)?(AM)?([ -+\d]{4,7}|[UTCESTGMT ]{2,4})*"%monthnames, re.I)

并且正则表达式快速返回,打印出来:

[('', '', '2003'), ('', '', '1973'), ('', 'AM', ' 1973'), ('', '', '1981"')]
['2003-08-06', '2003-08-07', '2003-07-23', '1973-01-18', '3/14/1973', '16/6/1981', '16/6/1981', '16/6/1981', '16/6/1981'
, '08/08/2003']

虽然我不知道这是不是你想要的。