我的代码执行以下操作:
n
个字词,在关键字右侧返回n
个字词。注意:在此上下文中,“单词”是任何非空格字符串。 “$ 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:]
这是令人讨厌的罪魁祸首:
print search("\$27.5 million", document, 10)
以下是测试此代码的方法: 从上面的代码块中复制函数定义并运行以下命令:
t = "The world is a small place, we $.205% try to take care of it."
print search("\$.205", t, 3)
我怀疑我有一个令人讨厌的灾难性回溯案例,但我现在太新了,无法指出我的问题。
如何加快代码速度?
答案 0 :(得分:4)
如果只搜索固定字符串,使用re.search
(甚至string.find
)来查找字符串,没有任何周围的捕获组。然后在重新匹配对象上使用匹配的位置和长度(.start
和.end
,或者find
的返回值加上搜索字符串的长度)。在匹配之前获取子字符串并在其上执行/\s*(\S*)\s*\z/
等,并在匹配后获取子字符串并在其上执行/\A\s*(\S*)\s*/
等。
另外,有关回溯的帮助:您可以使用\s+\S+\s+
之类的模式而不是\s*\S*\s*
(两个空格块必须用非零分隔非空格的数量,否则它们不会是两个块),你不应该像你一样连续两个\s*
。我认为r'\S+'.join([[r'\s+']*(n))
会为捕获n
之前的单词提供正确的模式(但我的Python生锈了,所以检查一下)。
答案 1 :(得分:1)
我在这里看到了几个问题。第一个也可能是最糟糕的是,“环绕”正则表达式中的所有内容都是可选的,而不仅仅是可选的,而且独立可选。鉴于此字符串:
"Lorem ipsum tritani impedit civibus ei pri"
...当searchText = "tritani"
和n = 1
时,这是它在找到第一场比赛之前必须经历的事情:
regex: \s* \S* \s* tritani
offset 0: '' 'Lorem' ' ' FAIL
'' 'Lorem' '' FAIL
'' 'Lore' '' FAIL
'' 'Lor' '' FAIL
'' 'Lo' '' FAIL
'' 'L' '' FAIL
'' '' '' FAIL
...然后它突然前进一个位置并重新开始:
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多步中找到了第一场比赛。这是最常见的正则表达式反模式之一:当您使用*
时使用+
。换句话说,如果它不是可选的,请不要将视为可选项。
最后,您可能已经注意到\s*\s*
自动生成的正则表达式
答案 2 :(得分:0)
你可以尝试使用mmap
和适当的正则表达式标志,例如(未经测试):
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
这只会让内存使用率降低......
替代方案是有一个单词的滑动窗口(前后单个单词的简单示例)......:
import re
import mmap
from itertools import islice, tee, izip_longest
with open('testingdata.txt') as fin:
mf = mmap.mmap(fin.fileno(), 0, access=mmap.ACCESS_READ)
words = (m.group() for m in re.finditer('\w+', mf, flags=re.DOTALL))
grouped = [islice(el, idx, None) for idx, el in enumerate(tee(words, 3))]
for group in izip_longest(*grouped, fillvalue=''):
if group[1] == 'something': # check criteria for group
print group
答案 3 :(得分:-1)
我认为你完全倒退了(我对你刚才做的事情感到有点困惑!)
我建议查看我在cloud toolbox
的textools模块中开发的re_search
函数
使用re_search你可以解决这个问题:
from cloudtb import textools
data_list = textools.re_search('my match', pdf_text_str) # search for character objects
# you now have a list of strings and RegPart objects. Parse through them:
for i, regpart in enumerate(data_list):
if isinstance(regpart, basestring):
words = textools.re_search('\w+', regpart)
# do stuff with words
else:
# I Think you are ignoring these? Not totally sure
以下是有关如何使用及其工作原理的链接: http://cloudformdesign.com/?p=183
除此之外,您的正则表达式也将以更易读的格式打印出来。
您可能还想查看我的工具Search The Sky或类似工具Kiki,以帮助您构建和理解正则表达式。