Оптимизация скорости многократного повторного сопоставления в больших файлах

#python-3.x #regex

#python-3.x #регулярное выражение

Вопрос:

Изменить: Добавить аннотацию содержимого файла

Для нескольких больших файлов (gt; 10 МБ), хранящихся в виде списков, мне нужно выполнить различное сопоставление, захватить и использовать соответствующие сопоставленные данные с помощью gt; group() .

При этом я сталкиваюсь с проблемой производительности. Использование re.compile() экономит мне 3 фактора, но этого недостаточно.

Вот что я делаю на данный момент:

 import re  results = [  'EXTRACT for DC ANALYSIS',  ' PARAM VREF = 1.0500E 00',  ' TEMPERATURE = 2.5000E 01 Celsius',  ' ICARLO = 9999',  ' *VREF_INPUT = 1.0500E 00 Volts',  ' *VREFSENSEANA = 2.1184E-01 Volts',  ' *IREFCOMPANA = 1.7614E-05',  ' *VOFFSET = 1.9432E-03 Volts',  ' *IRATIO_COMP_PBIAS_DIFF__COMP_PIREFCOMP = 2.1124E 00',  ' *IRATIO_COMP_PBIAS_OUT__COMP_PIREFCOMP = 1.0503E 00',  '',  'EXTRACT for DC TRANSFER CURVES',  ' PARAM VREF = 1.0500E 00',  ' TEMPERATURE = 2.5000E 01 Celsius',  ' ICARLO = 10000',  ' *VREF_INPUT = 1.0500E 00 Volts',  ' *VREFSENSEANA = 2.1249E-01 Volts',  ' *IREFCOMPANA = 1.6552E-05',  ' *VOFFSET = 2.8657E-03 Volts',  ' *IRATIO_COMP_PBIAS_DIFF__COMP_PIREFCOMP = 2.0130E 00',  ' *IRATIO_COMP_PBIAS_OUT__COMP_PIREFCOMP = 1.0142E 00',  ' *MC_501(VREF_INPUT) = 0.0',  ' *MC_502(VREF_INPUT) = 1.0000E 00',  ' *MC_600(VREF_INPUT) = 1.0500E 00',  ' *MC_907(VREF_INPUT) = FAILED',  ' *MC_908(VREF_INPUT) = 0.0', ]  re_analysis = re.compile(r's*EXTRACT for (w )') re_param = re.compile(r's*PARAMs (w )s*=s*(S )') re_alter = re.compile(r's*ALTER index (d )s (w )') re_extract = re.compile(r's**(w )s*=s*(S )') re_extract_mc = re.compile(r's**MC_(d )((w ))s*=s*(S )') re_icarlo = re.compile(r's*ICARLOs*=s*(d )')  for line in results: # self.result is the file stored as list   match_analysis = re_analysis.match(line)  match_param = re_param.match(line)  match_alter = re_alter.match(line)  match_extract = re_extract.match(line)  match_extract_mc = re_extract_mc.match(line)  match_icarlo = re_icarlo.match(line)  # do some stuff with the various match and their group()  

Общий процесс занимает ~0,5 секунды для данного справочного файла, 0,35 секунды-это вычисления 6 совпадений.

Я хочу сильно сократить это время выполнения матча на 0,35 секунды.

Существуют ли альтернативные способы «построить» 6 матчей по-разному, чтобы быть быстрее?

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

Комментарии:

1. Похоже, что, по крайней мере, некоторые из этих моделей являются взаимоисключающими. Вы можете попытаться объединить их в один шаблон, используя именованные группы.

2. Есть ли много линий, self.results которые не соответствуют ни одному из ваших 6 шаблонов? Другими словами, есть ли много строк, которые нужно отбросить?

3. @Оливер, не могли бы вы предоставить несколько входных данных, чтобы мы могли протестировать ваш исходный код? Спасибо

4. @MegaIng: Я рассматриваю ваше предложение. На данный момент я не знаком с названными группами

5. @CasimiretHippolyte: подавляющее большинство строк будет соответствовать одному шаблону, за исключением пустых строк и некоторых строк заголовка

Ответ №1:

Я нашел способ построить одно регулярное выражение из 6 следующих предложений @MegaIng для использования именованных групп.

К сожалению, время выполнения не так сильно отличается от моего первоначального решения, вероятно, из-за сложности регулярных выражений? Так что, по крайней мере, с этой реализацией это не сильно помогает.

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

 # sorry in advance for the eyes ... regex = re.compile(r's*(?Plt;keywordgt;EXTRACT|PARAM|ALTER index|*MC_d |*|ICARLO)s*(*(?Plt;namegt;w )*)*s*=*s*(?Plt;valuegt;S )')  for line in results:   match = regex.match(line)  if match:  _reg = match.groupdict()  else:  continue  # do stuff using keyword key to known which keyword is seen   if _reg['keyword'] == 'PARAM':  # some stuff with name amp; value keys  

И groupdict() результат строка за строкой для моего примера списка:

 {'keyword': 'EXTRACT', 'name': 'for', 'value': 'DC'} {'keyword': 'PARAM', 'name': 'VREF', 'value': '1.0500E 00'} {'keyword': 'ICARLO', 'name': None, 'value': '9999'} {'keyword': '*', 'name': 'VREF_INPUT', 'value': '1.0500E 00'} {'keyword': '*', 'name': 'VREFSENSEANA', 'value': '2.1184E-01'} {'keyword': '*', 'name': 'IREFCOMPANA', 'value': '1.7614E-05'} {'keyword': '*', 'name': 'VOFFSET', 'value': '1.9432E-03'} {'keyword': '*', 'name': 'IRATIO_COMP_PBIAS_DIFF__COMP_PIREFCOMP', 'value': '2.1124E 00'} {'keyword': '*', 'name': 'IRATIO_COMP_PBIAS_OUT__COMP_PIREFCOMP', 'value': '1.0503E 00'} {'keyword': 'EXTRACT', 'name': 'for', 'value': 'DC'} {'keyword': 'PARAM', 'name': 'VREF', 'value': '1.0500E 00'} {'keyword': 'ICARLO', 'name': None, 'value': '10000'} {'keyword': '*', 'name': 'VREF_INPUT', 'value': '1.0500E 00'} {'keyword': '*', 'name': 'VREFSENSEANA', 'value': '2.1249E-01'} {'keyword': '*', 'name': 'IREFCOMPANA', 'value': '1.6552E-05'} {'keyword': '*', 'name': 'VOFFSET', 'value': '2.8657E-03'} {'keyword': '*', 'name': 'IRATIO_COMP_PBIAS_DIFF__COMP_PIREFCOMP', 'value': '2.0130E 00'} {'keyword': '*', 'name': 'IRATIO_COMP_PBIAS_OUT__COMP_PIREFCOMP', 'value': '1.0142E 00'} {'keyword': '*MC_501', 'name': 'VREF_INPUT', 'value': '0.0'} {'keyword': '*MC_502', 'name': 'VREF_INPUT', 'value': '1.0000E 00'} {'keyword': '*MC_600', 'name': 'VREF_INPUT', 'value': '1.0500E 00'} {'keyword': '*MC_907', 'name': 'VREF_INPUT', 'value': 'FAILED'} {'keyword': '*MC_908', 'name': 'VREF_INPUT', 'value': '0.0'}  

Ответ №2:

Я, наконец, нахожу хорошее решение, используя базовое разделение в каждой строке вместо регулярного выражения.

Каждую строку можно прочитать как предложение, что делает ее довольно простой. Все необходимые элементы строки могут быть захвачены с ее индексом в списке или с использованием последнего индекса [-1]

 for line in results:  if line == '':  continue  _elems = line.split()   if _elems[0].startswith('*MC_'):  # do stuff   # ...   if _elems[0] == 'PARAM':  # do stuff  

Общее время выполнения при этом составляет 0,23 секунды, а при разделении-около 0,07 секунды. Чистая прибыль в 5 раз больше.