寻找优化Python文本解析速度的实用方案
我需要处理一组文本行,并用不同的命名正则表达式来解析它们。
每一行都会通过每个正则表达式,直到找到匹配的内容为止。 一旦找到匹配,代码就应该返回(命名的正则表达式,值)这样的配对,针对每一行。
因为文件的大小超过2GB,所以我在寻找一些提高解析速度的想法。
目前代码是用Python写的,但这部分是可以改变的。 一个选择是把所有代码转换成C语言,这样可以利用PCRE获得更快的速度和更快的输入输出,但这条路比较慢,而且将来维护起来会比较困难。
我在寻找一些实际的解决方案,比如:
- 把解析器转换成更快的语言
- 转向使用Cython(?)
- 把文件分成多个小块,然后在几个线程上运行
3 个回答
我首先想到的是,你应该使用 re.compile() 来准备所有的正则表达式。不过我想这可能已经做过了!
另一个需要考虑的点是分层处理:
根据你使用的正则表达式类型,它们可以按照层级组织。这样你可以先过滤掉一些行,然后再把需要检查的行传给正则表达式。
举个例子,如果你在正则表达式中搜索像 "< body attr1 attr2>" 这样的标签,先在这一行中搜索 "body" 可能会有帮助,这样在确认它真的是一个标签之前就能先过滤掉不相关的行。你也可以在使用与标签相关的正则表达式之前,先搜索 "<"。
如果你把多个表达式合并成一个并进行匹配,也可能会加快处理速度。比如把对 "{term1}" 和 "{term2}" 的单独检查替换成 "{(term1|term2)}"。
接着,你可以通过定义它们能处理的最小行长度来过滤需要检查的正则表达式。
像这样的简单方法可以在不换语言或更换CPU的情况下,加快你的应用程序运行速度。
如果可能的话,你还可以尝试多线程处理。根据CPU的可用性,你可以在两个线程中处理每隔一行,这样也可能大幅提高处理速度。
在Python中,加快模式匹配的一个重要方法是尽量使用一个正则表达式。也就是说,不要对每一行输入都用一堆正则表达式去逐个检查,而是把这些正则表达式合并成一个,用|
符号连接起来,然后对每一行只应用这个合并后的正则表达式一次:
reg = re.compile(r'''
(?<patt1>foo)
|(?<patt2>bar)
''', re.VERBOSE)
for line in lines:
mo = reg.search(line)
if mo:
yield (mo.lastgroup(), mo.group(mo.lastgroup()))
首先,我觉得不需要太担心换成其他编程语言。其实,尝试其他策略可能会带来更大的好处。毕竟,Python使用的正则表达式引擎是用C语言写的(如果我没记错的话)。
- 优化正则表达式可能是你首先要做的事情。怎么优化取决于你的文本和表达式。可以去看看一些文章和例子。@ThomasH也提到了一个很好的观点。
- 优化正则表达式最简单的方法就是把它去掉;看看有没有机会用其他测试方法,比如
x in y
或者line.endswith()
等等。 - 试着用pypy来运行你的代码。这样可以在不修改代码的情况下提高性能。不过在你的情况下,性能提升可能不明显,因为这还是受限于你的正则表达式和文件输入输出的速度。
- 在多个线程或进程中同时处理文件可能会有帮助。我建议用一个进程来读取文件,然后把行推送到一个队列中,多个进程可以从这个队列中取数据。有很多方法可以做到这一点。可以看看这个链接。