Python正则表达式:搜索()对于大型文本文件非常慢

2024-05-29 10:53:19 发布

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

我的代码执行以下操作:

  1. 取一个大的文本文件(即一份300页的PDF格式的法律文件)。在
  2. 找到某个关键词(例如“small”)。在
  3. 在关键字的左边返回n个单词,右边返回n个单词。在

注意:在此上下文中,“word”是任何非空格字符的字符串。”“cow123美元”将是一个词,但“医疗保健”将是两个词。在

这是我的问题: 代码在300页上运行需要非常长的时间,而且随着n的增加,时间往往会迅速增加。在

这是我的代码:

fileHandle = open('test_pdf.txt', mode='r')
document = fileHandle.read()

def search(searchText, doc, n):
#Searches for text, and retrieves n words either side of the text, which are returned separately

    surround = r"\s*(\S*)\s*"
    groups = re.search(r'{}{}{}'.format(surround*n, searchText, surround*n), doc).groups()
    return groups[:n],groups[n:]

这是罪魁祸首:

^{pr2}$

下面是如何测试此代码: 从上面的代码块复制函数定义并运行以下命令:

t = "The world is a small place, we $.205% try to take care of it."
print search("\$.205", t, 3)

我怀疑我遇到了灾难性的回溯,但我对regex太陌生了,无法指出问题所在。在

如何加速我的代码?


Tags: of代码textsearchdocpdf时间单词
3条回答

使用re.search(如果只搜索固定字符串,甚至使用string.find)来查找字符串,而不使用任何周围的捕获组。然后使用匹配的位置和长度(.start和{}在重新匹配对象上,或者使用find的返回值加上搜索字符串的长度)。在匹配之前获取子字符串并对其执行/\s*(\S*)\s*\z/等操作,在匹配之后获取子字符串并对其执行/\A\s*(\S*)\s*/等操作。在

另外,对于回溯的帮助:您可以使用类似\s+\S+\s+而不是\s*\S*\s*(两块空白必须用非空白的非零数量分隔开,否则它们不会是两块),而且您不应该像您那样将两个连续的\s*对接起来。我认为r'\S+'.join([[r'\s+']*(n))将提供正确的模式来捕获n前面的单词(但是我的Python已经生锈了,所以检查一下)。在

您可以尝试使用mmap和适当的regex标志,例如(未测试):

import re
import mmap

with open('your file') as fin:
    mf = mmap.mmap(fin.fileno(), 0, access=mmap.ACCESS_READ)
    for match in re.finditer(your_re, mf, flags=re.DOTALL):
        print match.group() # do something with your match

这只会降低内存使用率。。。在

另一种选择是有一个滑动窗口的单词(简单的例子,只有一个单词在前面和后面)…:

^{pr2}$

我在这里看到了几个问题。第一个,也可能是最糟糕的,是“surround”regex中的所有内容都是可选的,不仅是可选的,而且是独立的可选的。给定此字符串:

"Lorem ipsum tritani impedit civibus ei pri"

…当searchText = "tritani"n = 1时,它在找到第一个匹配项之前必须经过以下步骤:

^{pr2}$

…然后它向前冲了一个位置,然后重新开始:

offset 1:   ''   'orem'   ' '    FAIL
            ''   'orem'   ''     FAIL
            ''   'ore'    ''     FAIL
            ''   'or'     ''     FAIL
            ''   'o'      ''     FAIL
            ''   ''       ''     FAIL

。。。等等。根据RegexBuddy的调试器,要达到偏移量大约需要150个步骤,才能使第一个匹配:

position 5: ' '  'ipsum'  ' '    'tritani'

只需跳过一个单词,并使用n=1。如果您设置n=2,则会得到以下结果:

\s*(\S*)\s*\s*(\S*)\s*tritani\s*(\S*)\s*\s*(\S*)\s*

我相信你能看到事情的发展。请特别注意,当我将其更改为:

(?:\s+)(\S+)(?:\s+)(\S+)(?:\s+)tritani(?:\s+)(\S+)(?:\s+)(\S+)(?:\s+)

…它在20多步中找到第一个匹配。这是最常见的regex反模式之一:在应该使用+时使用*。换句话说,如果它不是可选的,就不要把它当作可选的。在

最后,您可能已经注意到\s*\s*自动生成的regex

相关问题 更多 >

    热门问题