Python: эффективная проверка файла построчно
У меня есть очень большие текстовые файлы (около 1,5 миллионов или более строк), чтобы проанализировать некоторую информацию. В каждой строке я проверяю определенные ключевые слова (я называю их "флажками").
Тем не менее, для каждой строки выполняются десятки проверок, поэтому моей программе требуется значительное время для завершения. Есть ли более быстрый способ проверки? Ниже приведен пример того, что я делаю:
nameFound = false
ageFound = false
for line in file:
if not nameFound and line.find('name:') != -1:
do something
elif not ageFound and line.find('age:') != -1:
do something
elif line.find('test pass') != -1:
do something
elif line.find('test fail') != -1:
do something
and so on ...
Некоторые из "флагов" появляются только один раз (хотя я не знаю, где в файле или в каком порядке), поэтому я использую переменную "найдено" для короткого замыкания моих проверок на эти "флаги". Другая информация появляется тысячи раз, поэтому я не могу использовать переменную "found" в моих условиях. Одно я знаю, что каждая строка, если она содержит "флаг", будет иметь только один флаг максимум. Имея в виду эту информацию, есть ли более эффективный способ сделать это?
4 ответа
Просто предложение: вы можете использовать список "флагов" и перебирать их.
flags = [('name:', nameMethod), ('age:', ageMethod), ('test pass', tpMethod), ('test fail', tfMethod), ... ] #methods are functions without ()
for line in lines:
for flag, func in flags:
if line.find(flag) != -1:
func(args) #your args
break #same functionality as elif
Кстати, используя in
оператор лучше чем str.find
или же str.index
, Так if flag in line: #do something
,
Это только для удобства чтения. Вы должны определенно выполнить профилирование, чтобы выявить узкие места, а затем посмотреть, что следует / не следует исправлять. Я точно знаю, что с помощью in
вместо str.find
гораздо эффективнее (примерно в 3 раза по моим оценкам тестирования).
В Python мало что можно сделать, чтобы ускорить процесс. Вы идете построчно, читая файл только один раз. Вы уже сделали много, чтобы "выжать" больше скорости из своего контура с коротким замыканием. Время, необходимое вашему коду для запуска, пропорционально размеру файла.
Может быть, лучше сначала отфильтровать строки файла с помощью более быстрого инструмента. Например, grep
Команда Unix, как правило, реализована на C и очень быстра (намного быстрее, чем реализация цикла в Python). Таким образом, вы можете использовать такую команду (в Linux или Unix), чтобы отфильтровать строки, а затем запустить вашу программу на отфильтрованном файле:
grep "flag1\|flag2\|flag3" big.txt > filtered.txt
Другим вариантом может быть использование Cython, чтобы попытаться ускорить ваш код, скомпилировав его в C. Но в действительности это не принесет вам такой большой пользы.
Поиск по регулярному выражению может быть быстрее:
import re
flags = "name|age|etc"
for line in file:
m = re.match(flags, line)
if m.group():
do_something(m)
В качестве альтернативы вы можете поместить все свои тесты в цикл while, чтобы он завершился, как только обнаружит флаг:
flags = ['name', 'age', 'foo']
for line in file:
i = 0
while i < len(flags):
if (line.find(flags[i])):
do_something(flags[i])
i = len(flag[i])
i += 1
Если вам нужно сделать что-то свое для каждого флага, вы можете поместить эту логику в метод.
Учтите это: каждый if
ищет во всей строке (возможно, несуществующий) флаг.
Предположим, что ваша строка была "Папа достиг аэропорта", а ваш список флагов был ["М", "Мама"], тогда очевидно, что если М не может быть найден, то и Маму не найдут.
Если вы готовы предпринять усилия для их реализации, алгоритмы Aho-Corasick или Rabin-Karp обеспечат значительное ускорение.