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 обеспечат значительное ускорение.

Другие вопросы по тегам