我正在使用以下代码,其中我有一个字典文件, Dictionary.txt
和一个搜索文本文件 SearchText.csv
,并且我正在使用Regex查找和存储匹配的关键字并计数它们并计数它们。
我有一个问题:其中一些文件是成千上万的关键字,并且需要太多时间来处理。我在一个具有300,000个关键字的字典上运行代码,一个小时后没有写一排。
那么,我该怎么做才能减少此过程的运行时间?
import csv
import time
import re
allCities = open('Dictionary.txt', encoding="utf8").readlines()
timestr = time.strftime("%Y-%m-%d-(%H-%M-%S)")
with open('SearchText.csv') as descriptions,open('Result---' + str(timestr) + '.csv', 'w', newline='') as output:
descriptions_reader = csv.DictReader(descriptions)
fieldnames = ['Sr_Num', 'Search', 'matched Keywords', 'Total matches']
output_writer = csv.DictWriter(output, delimiter='|', fieldnames=fieldnames)
output_writer.writeheader()
line=0
for eachRow in descriptions_reader:
matches = 0
Sr_Num = eachRow['Sr_Num']
description = eachRow['Text']
citiesFound = set()
for eachcity in allCities:
eachcity=eachcity.strip()
if re.search('\b'+eachcity+'\b',description,re.IGNORECASE):
citiesFound.add(eachcity)
matches += 1
if len(citiesFound)==0:
output_writer.writerow({'Sr_Num': Sr_Num, 'Search': description, 'matched Keywords': " - ", 'Total matches' : matches})
else:
output_writer.writerow({'Sr_Num': Sr_Num, 'Search': description, 'matched Keywords': " , ".join(citiesFound), 'Total matches' : matches})
line += 1
print(line)
print(" Process Complete ! ")
这是Dictionary.txt
的一些行的示例:
les Escaldes
Andorra la Vella
Umm al Qaywayn
Ras al Khaimah
Khawr Fakkn
Dubai
Dibba Al Fujairah
Dibba Al Hisn
Sharjah
Ar Ruways
作为用户Martineau评论,最好配置代码以确定优化在哪里最有效,并测量任何尝试优化的效果。
在没有任何分析数据的情况下,最佳优化候选者可能是这种内部循环:
for eachcity in allCities:
eachcity=eachcity.strip()
if re.search('\b'+eachcity+'\b',description,re.IGNORECASE):
citiesFound.add(eachcity)
在这里,代码在allCities
中的每个字符串上调用strip
-可以在循环外完成一次,然后为每个城市调用re.search
。
使用|
Metacharacter将所有城市组合到单个正则态度可能更有效,该元时间表示替代匹配。例如模式
r'foo|bar'
将匹配'foo'
或 'bar'
。
这是一个简单的示例:
>>> text = 'I am flying from les Escaldas to Dubai via Sharjah on Monday'
>>> cities = ['Sharjah', 'Dubai', 'les Escaldas', 'Ar Ruways']
>>> pattern = r'|'.join(r'b{}b'.format(re.escape(city)) for city in cities)
>>> pattern
'\bSharjah\b|\bDubai\b|\bles Escaldas\b|\bAr Ruways\b'
>>> matches = re.findall(pattern, text)
>>> print(matches)
['les Escaldas', 'Dubai', 'Sharjah']
在每个城市名称上调用re.escape
,如果城市名称之一包含一个也是正则表达式的字符,则可以防止更改搜索模式。
将此技术应用于问题:
# We only need to create the regex pattern once,
# so do it outside the loop.
pattern = r'|'.join(r'b{}b'.format(re.escape(city.strip())) for city in allCities)
for eachRow in descriptions_reader:
Sr_Num = eachRow['Sr_Num']
description = eachRow['Text']
citiesFound = set()
found = re.findall(pattern, description, re.IGNORECASE)
citiesFound.update(found)
matches = len(found)
文档中描述了 |
metacharacter的行为。
由'|'分开从左到右尝试。当一种模式完全匹配时,该分支被接受。这意味着,一旦A匹配,B也不会进一步测试,即使它会产生更长的整体匹配。
因此,如果有潜在的匹配项是其他候选人的子字符串(例如"迪拜"one_answers"迪拜市"),则候选人必须较早出现在模式中,否则发动机将找到较短的候选人并将其作为比赛归还。
为了防止这种情况,请在创建模式之前按长度顺序排序allCities
:
allCities.sort(key=len, reverse=True)
或使用sorted
如果必须保留allCities
的顺序:
pattern = r'|'.join(r'b{}b'.format(re.escape(city.strip())) for
city in sorted(allCities, key=len, reverse=True))
您可以使用集合而不是城市名称列表,然后在空间上拆分描述以隔离单词。这可能比正则表达式更快
例如:
...
allCities = open('Dictionary.txt', encoding="utf8").readlines()
citySet = set([city.lower().strip() for city in allCities]
...
...
citiesFound = set([ word for word in description.split(" ") if word.lower() in citySet ])
...