Проблема:

Замена нескольких строковых шаблонов в большом текстовом файле занимает много времени. (Python)

Сценарий:

У меня есть большой текстовый файл без особой структуры. Но он содержит несколько шаблонов. Например, адреса электронной почты и номера телефонов.

В текстовом файле содержится более 100 различных таких шаблонов, а размер файла - 10 МБ (размер может увеличиваться). Текстовый файл может содержать или не содержать все 100 шаблонов.

В настоящее время я заменяю совпадения с помощью re.sub(), и подход к выполнению замены выглядит, как показано ниже.

readfile = gzip.open(path, 'r') # read the zipped file
lines = readfile.readlines() # load the lines 

for line in lines:
    if len(line.strip()) != 0: # strip the empty lines
        linestr += line

for pattern in patterns: # patterns contains all regex and respective replaces
    regex = pattern[0]
    replace = pattern[1]
    compiled_regex = compile_regex(regex)
    linestr = re.sub(compiled_regex, replace, linestr)

Этот подход требует много времени для больших файлов. Есть ли лучший способ его оптимизировать?

Я подумываю заменить += на .join(), но не уверен, насколько это поможет.

1
Sanath Kumar 17 Дек 2016 в 00:52
У вас есть шаблоны регулярных выражений или простые строки?
 – 
michaJlS
17 Дек 2016 в 00:57
Если у вас такой большой файл, вы также можете один раз отсортировать данные с помощью первичного ключа, а затем просто выполнить двоичный поиск, что значительно повысит производительность. Это разовый компромисс, и мне кажется, что это быстрая победа. Кроме того, при таком размере следует рассмотреть возможность использования базы данных. Если вы имеете дело с большим количеством данных, применение к ним структуры почти всегда дает значительное улучшение. Отсюда причина того, что университеты часто преподают структуры данных как единый курс.
 – 
FMaz
17 Дек 2016 в 00:59
@Krazor: Автор вопроса говорит, что у файла нет структуры. Так что мне интересно, как вы думаете об этом?
 – 
Bill Bell
17 Дек 2016 в 01:02
2
 – 
Robᵩ
17 Дек 2016 в 01:06
1
Тогда извините меня. Вы обязательно должны, как упоминал @salah, рассмотреть возможность использования генератора!
 – 
FMaz
17 Дек 2016 в 01:37

2 ответа

Вы можете использовать lineprofiler, чтобы определить, какие строки в вашем коде занимают больше всего времени

pip install line_profiler    
kernprof -l run.py

Еще одна вещь, я думаю, вы создаете слишком большую строку в памяти, возможно, вы можете использовать генераторы

2
salah 17 Дек 2016 в 01:13
В этом контексте имеет смысл использовать генератор. Я не понимаю, чем поможет лайнпрофиль?
 – 
FMaz
17 Дек 2016 в 01:27
Спасибо, генераторы не особо разбираюсь. Я займусь этим. Итак, по вашему мнению, разбиение текстового файла на части и выполнение замен регулярных выражений может быть более оптимизировано?
 – 
Sanath Kumar
17 Дек 2016 в 01:36
1
Я не совсем уверен, будет ли это быстрее, но определенно будет эффективнее.
 – 
FMaz
17 Дек 2016 в 02:02

Вы можете получить немного лучшие результаты, если:

large_list = []

with gzip.open(path, 'r') as fp:
    for line in fp.readlines():
        if line.strip():
            large_list.append(line)

merged_lines = ''.join(large_list)

for regex, replace in patterns:
    compiled_regex = compile_regex(regex)
    merged_lines = re.sub(compiled_regex, replace, merged_lines)

Однако дальнейшая оптимизация может быть достигнута, зная, какой тип обработки вы применяете. Фактически, последняя строка будет занимать всю мощность процессора (и выделение памяти). Если регулярные выражения можно применять к каждой строке, вы можете добиться отличных результатов, используя пакет многопроцессорной обработки. Многопоточность вам ничего не даст из-за GIL (https://wiki.python.org/moin/ GlobalInterpreterLock)

1
Pierre Alex 17 Дек 2016 в 01:27
Я разделяю ваши мысли о многопроцессорности. И мой сценарий включает в себя как применение регулярных выражений по строкам, так и по строкам (фиксированная структура).
 – 
Sanath Kumar
17 Дек 2016 в 01:48
Было бы слишком дорого объединить все строки, выполнить сопоставление регулярных выражений в merged_lines и затем снова разделить их для выполнения сопоставлений для каждой строки? Потому что может быть несколько блоков текстовых шаблонов, которые можно заменить, и это уменьшит длину файла для анализа строка за строкой.
 – 
Sanath Kumar
17 Дек 2016 в 01:51
Вы можете протестировать различные варианты - следующее, что вы можете сделать, это определить узкое место (ЦП -> попробуйте многопроцессорность, разделив исходный файл или рабочий процесс; ввод-вывод -> сначала загрузите все в память)
 – 
Pierre Alex
1 Янв 2017 в 17:37