我有一个仅包含3500行的文件,如下所示:

filecontent= "13P397;Fotostuff;t;IBM;IBM lalala 123|IBM lalala 1234;28.000 things;;IBMlalala123|IBMlalala1234"


然后我想从filecontent中获取与某个字符串匹配的每一行(使用python 2.7):

this_item= "IBMlalala123"
matchingitems =  re.findall(".*?;.*?;.*?;.*?;.*?;.*?;.*?"+this_item,filecontent)


每个findall需要17秒。我需要在这3500行中搜索4000次。它需要永远。知道如何加快速度吗?

评论

在Python中,无法避免在正则表达式中回溯。如果您无法通过修改正则表达式来解决问题,请尝试在大字符串上使用非正则表达式str.split,并在单个小字符串上运行正则表达式。

如果可以的话,作为一般规则,请尽量避免非贪婪的匹配(即。*?中的?)。如果正则表达式可以简单地匹配它们想要的任何内容,而又不考虑找到可行的最小匹配项,则它们更容易匹配。在某些情况下,找到“贪婪模式”变得非常复杂,在这种情况下,这取决于您所需的速度和想要实现的可读性。

下面有几个很好的答案,但是我很好奇编译正则表达式的影响。

看起来您遇到了O(n ^ 2)“边缘保护套”,用于回溯正则表达式引擎:swtch.com/~rsc/regexp/regexp1.html

@kojiro:不多。使用其他正则表达式引擎有很大帮助。 Tcl在24.73725微秒内与我的计算机上的正则表达式匹配。

#1 楼

.*?;.*?将导致灾难性的回溯。有关此问题的更多详细信息,请参见此帖子:http://www.regular-expressions.info/catastrophic.html

要解决性能问题,请删除.*?;并将其替换为[^;]*;,即快得多。

评论


\ $ \ begingroup \ $
非常感谢。一开始我没有得到事实,我必须用您的新元素替换每个旧元素。工作,当我那样做:D
\ $ \ endgroup \ $
–迈克
13-10-9 13:35

\ $ \ begingroup \ $
另外,您应该在这里做的另一件事,而不是像[^;] *; [^;] *; [^;] *; [^;] *; [^;] * ; [^;] *; [^;] *,应将其缩小为更简洁的内容,例如[^;] *(?:; [^;] *){6}。
\ $ \ endgroup \ $
– AJMansfield
2013年10月9日14:11

#2 楼


有些人遇到问题时会认为“我知道,我会使用正则表达式”。现在他们有两个问题。 -Jamie Zawinski


有几点要评论:


正则表达式可能不是正确的工具。
.*?;.*?;.*?;.*?;.*?;.*?;.*?"可能非常慢,可能无法执行您想要的操作(它可能比您想要的匹配更多;)。 [^;]*;很可能会做您想要的。


评论


\ $ \ begingroup \ $
实际的表达式是:。*?;。*?;。*?;。*?;。*?;。*?;。*?IBMlalala123介意更明确吗?我尝试了一些变体将您的版本替换为您的版本,但是失败了...(应该返回整行,[^;] *; IBMlalala123仅返回id字符串)
\ $ \ endgroup \ $
–迈克
13-10-9在8:14



#3 楼

像这样使用split:

>>> filecontent = "13P397;Fotostuff;t;IBM;IBM lalala 123|IBM lalala 1234;28.000 things;;IBMlalala123|IBMlalala1234";
>>> items = filecontent.split(";");
>>> items;
['13P397', 'Fotostuff', 't', 'IBM', 'IBM lalala 123|IBM lalala 1234', '28.000  things', '', 'IBMlalala123|IBMlalala1234']
>>> 


我不确定您要在最后一步中执行什么操作,但是也许是这样的事情?

>>> [(i, e) for i,e in enumerate(items) if 'IBMlalala123' in e]
[(7, 'IBMlalala123|IBMlalala1234')]
>>> 


更新:
如果第二次尝试符合您的要求:要查找文件中所有以'IBMlalala123'作为分号分隔的字段的行,请执行以下操作:

>>> with open('big.file', 'r') as f:
>>>   matching_lines = [line for line in f.readlines() if 'IBMlalala123' in line.split(";")]
>>> 


评论


\ $ \ begingroup \ $
+1:在这种情况下,拆分通常比正则表达式快得多。特别是如果需要向后使用捕获的字段值。
\ $ \ endgroup \ $
– kriss
13年10月9日在16:17

\ $ \ begingroup \ $
是的,+ 1表示拆分。正则表达式似乎不是这项工作的最佳工具。
\ $ \ endgroup \ $
–乔什·安德森(Josh Anderson)
13-10-9在18:36

\ $ \ begingroup \ $
在我的案例中,这是关于从每行(从几千行)中获取包含特定字符串的行。您的解决方案只提供了一部分功能,并且需要进行一些增强才能使用数千个文件。我想您会建议用'\ n'分割,然后用'if string in line'检查每一行,然后将其放入列表中?不知道这样会不会更快。
\ $ \ endgroup \ $
–迈克
13年13月13日在13:44

\ $ \ begingroup \ $
@Mike:好的,但是在您的示例中,我看不到任何换行符,您的意思是分号应该表示换行符吗?无论如何,没有“快速”的分割线方法。操作系统无法跟踪换行符的存储位置,因此扫描换行符是任何读取行的lib都可以工作的方式。但是,您可以通过逐行阅读来节省大量的内存。
\ $ \ endgroup \ $
–亚历山大·托斯汀
13年13月13日14:46



\ $ \ begingroup \ $
它不是在示例中,而是在它之前和之后的文本中;)...只有3500行的文件... ...想要从匹配特定字符串的文件内容中获取每一行。 ..
\ $ \ endgroup \ $
–迈克
13年11月14日在15:17

#4 楼

一些想法:

是否需要正则表达式?您想要包含该字符串的行,为什么不使用'in'?

如果您使用正则表达式来验证行格式,则可以在较便宜的'in'找到候选字符之后执行此操作减少正则表达式的使用次数。

如果确实需要正则表达式,那么替换'。?;'怎么办?与'[^;];' ?