使用正则表达式循环的更有效方法是什么?

时间:2012-12-17 13:57:41

标签: python regex list

我有一个名单列表,我用它来提取目标字符串列表。例如:

names = ['Chris', 'Jack', 'Kim']
target = ['Chris Smith', 'I hijacked this thread', 'Kim','Christmas is here', 'CHRIS']

output = ['Chris Smith', 'Kim', 'CHRIS']

所以到目前为止的规则是:

  • 不区分大小写
  • 无法匹配部分字词('即圣诞节/被劫持不应与Chris / Jack匹配)
  • 只要在符合上述条件的字符串中找到名称,字符串中的其他字词就可以了。

为实现此目标,另一位SO用户在this thread中建议了此代码:

[targ for targ in target_list if any(re.search(r'\b{}\b'.format(name), targ, re.I) for name in first_names)]

到目前为止,这种方法非常准确,但鉴于名称列表长度约为5,000,目标列表长度为20-100行,一些字符串长度不超过30个字符。

有关如何提高绩效的任何建议吗?

解决方案:两个基于正则表达式的解决方案都遭遇了OverflowErrors,所以很遗憾我无法测试它们。有效的解决方案(来自@ mglison的回答)是:

new_names = set(name.lower() for name in names)
[ t for t in target if any(map(new_names.__contains__,t.lower().split())) ]

这提供了极大的性能从15秒增加到1秒以下。

3 个答案:

答案 0 :(得分:5)

似乎你可以将它们全部合并为1个超级正则表达式:

import re

names = ['Chris', 'Jack', 'Kim']
target = ['Chris Smith', 'I hijacked this thread', 'Kim','Christmas is here', 'CHRIS']

regex_string = '|'.join(r"(?:\b"+re.escape(x)+r"\b)" for x in names)
print regex_string
regex = re.compile(regex_string,re.I)
print [t for t in target if regex.search(t)]

非正则表达式解决方案,只有在名称是单个字(无空格)时才有效:

new_names = set(name.lower() for name in names)
[ t for t in target if any(map(new_names.__contains__,t.lower().split())) ]

any表达式也可以写成:

any(x in new_names for x in t.lower().split())

any(x.lower() in new_names for x in t.split())

或另一个依赖set.intersection的变体(由@DSM建议):

[ t for t in target if new_names.intersection(t.lower().split()) ]

如果效果非常重要,您可以查看哪些效果最佳,否则选择您认为最容易阅读/理解的

*如果你正在使用python2.x,你可能想要使用itertools.imap而不是map如果你在上面的那条路线上进行懒惰的评估 - 它也是让我想知道python是否提供了一个懒惰的str.split,其性能与非懒惰版本相同......

答案 1 :(得分:4)

这是我能想到的最简单的一个:

[item for item in target if re.search(r'\b(%s)\b' % '|'.join(names), item)]

所有在一起:

import re

names = ['Chris', 'Jack', 'Kim']
target = ['Chris Smith', 'I hijacked this thread', 'Kim','Christmas is here', 'CHRIS']

results = [item for item in target if re.search(r'\b(%s)\b' % '|'.join(names), item)]

print results
>>> 
['Chris Smith', 'Kim']

为了提高效率,您可以先编译正则表达式。

regex = re.compile( r'\b(%s)\b' % '|'.join(names) )
[item for item in target if regex.search(item)]

修改

在考虑了问题并查看了一些评论后,我将“解决方案”修改为以下内容:

import re
names = ['Chris', 'Jack', 'Kim']
target = ['Chris Smith', 'I hijacked this thread', 'Kim','Christmas is here', 'CHRIS']
regex = re.compile( r'\b((%s))\b' % ')|('.join([re.escape(name) for name in names]), re.I )
results = [item for item in target if regex.search(item)]

结果:

>>> 
['Chris Smith', 'Kim', 'CHRIS']

答案 2 :(得分:-1)

你正在另一个循环中进行一个循环,迭代两个列表。这总能给你二次性能。

一个局部优化是编译每个名称正则表达式(这将使每个正则表达式更快地应用)。但是,最大的好处是将所有正则表达式组合成一个正则表达式,并将其应用于输入中的每个项目。请参阅@ mgilson的答案,了解如何做到这一点。之后,您的代码性能应线性扩展为O(M + N),而不是O(M * N)。