У меня медленное регулярное выражение Python, если я просто удалю последнюю строку регулярного выражения, скорость увеличится на два порядка! Вот мой воспроизводящий пример:
import re
import timeit
mystr = "14923KMLI MLI2010010206581258 0.109 b M M M 0 09 60+ "
basere = r"""
(?P<wban>[0-9]{5})
(?P<faaid>[0-9A-Z]{4})\s
(?P<id3>[0-9A-Z]{3})
(?P<tstamp>[0-9]{16})\s+
\[?\s*((?P<vis1_coef>\-?\d+\.\d*)|(?P<vis1_coef_miss>M))\s*\]?\s*
\[?(?P<vis1_nd>[0-9A-Za-z\?\$/ ])\]?\s+
((?P<vis2_coef>\d+\.\d*)|(?P<vis2_coef_miss>[M ]))\s+(?P<vis2_nd>[A-Za-z\?\$ ])\s+
...............\s+
\[?\s*((?P<drct>\d+)|(?P<drct_miss>M))\s+
((?P<sknt>\d+)|(?P<sknt_miss>M))\s+
((?P<gust_drct>\d+)\+?|(?P<gust_drct_miss>M))\s*\]?\s+
"""
additional = r"""
\[?((?P<gust_sknt>\d+)R?L?F*\d*\+?|(?P<gust_sknt_miss>M))\s*\]?\s+
"""
P1_RE = re.compile(basere + additional, re.VERBOSE)
P2_RE = re.compile(basere, re.VERBOSE)
for myre in ["P1_RE", "P2_RE"]:
statement = "%s.match('%s')" % (myre, mystr)
res = timeit.timeit(statement, "from __main__ import %s" % (myre,),
number=1000)
print('%s took %.9f per iteration' % (myre, res / 1000.))
# result on my laptop, python 2.6 and 3.3 tested
# P1_RE took 0.001489143 per iteration
# P2_RE took 0.000019991 per iteration
Таким образом, единственная разница между P1_RE
и P2_RE
- это дополнительное регулярное выражение. Есть идеи относительно того, что я делаю неправильно?
1 ответ
Это из-за возврата; что является важной причиной неэффективности регулярных выражений.
Последняя удаленная строка требует сопоставления хотя бы одной цифры или M
с последующими пробелами и многих других необязательных вещей по пути.
Предпоследняя строка:
((?P<gust_drct>\d+)\+?|(?P<gust_drct_miss>M))\s*\]?\s+
Изначально соответствует последней части, то есть 60+
, и последующим пробелам, ничего не оставляя для последней строки. Таким образом, регулярное выражение не может сопоставить и выполняет возврат по одному символу за раз, чтобы попытаться сопоставить последнюю строку. Дело в том, что есть много вещей, которые регулярное выражение пытается сопоставить, и для каждого символа, для которого регулярное выражение было выполнено с возвратом, есть все больше и больше попыток сопоставления.
Перед совпадением регулярного выражения оно фактически отклонило довольно много символов, с которыми ранее совпадали предыдущие строки регулярного выражения.
Простым примером может служить строка (10 а):
aaaaaaaaaa
И регулярное выражение:
a+a+a+a+a+a+a+a+a+a+
Первая часть регулярного выражения a+
будет соответствовать всей строке (потому что +
жадная), но затем ей нужно соответствовать большему количеству a
, потому что следующая часть регулярного выражения a+
. Затем движок выполняет возврат одного символа, так что первый a+
соответствует aaaaaaaaa
(9 a), а второй соответствует последнему a
.
Далее идет третий a+
, и поскольку больше нет a
для сопоставления, регулярное выражение вернет один символ назад. Поскольку второй a
не может выполнить возврат, это будет первый a+
, который соответствует и соответствует aaaaaaaa
(8 а), тогда второй a+
будет соответствовать последнему aa
. Теперь больше нет a
, поэтому второй a+
вернется, чтобы соответствовать предпоследнему a
, и, наконец, третий a+
может соответствовать последнему a
.
Посмотрите, сколько сделали первые 3 a+
? А теперь представьте, что вы перебираете все возможные совпадения, пока все не совпадет?
Обычно, если вам вообще не нужен возврат с возвратом, вы должны использовать атомарные группы и притяжательные квантификаторы, но python не поддерживает их, по крайней мере, не модуль re
(regex
поддерживает).
Возможно, вы захотите пересмотреть свое регулярное выражение и попытаться увидеть, чему оно действительно должно соответствовать, что действительно необязательно, особенно возможные пробелы в ваших группах захвата.
Похожие вопросы
Новые вопросы
python
Python — это мультипарадигмальный многоцелевой язык программирования с динамической типизацией. Он предназначен для быстрого изучения, понимания и использования, а также обеспечивает чистый и унифицированный синтаксис. Обратите внимание, что Python 2 официально не поддерживается с 01.01.2020. Если у вас есть вопросы о версии Python, добавьте тег [python-2.7] или [python-3.x]. При использовании варианта Python (например, Jython, PyPy) или библиотеки (например, Pandas, NumPy) укажите это в тегах.
/trivial/
vs/trivial<withAdditional>/
и снова проверьте разницу. Как написано, ваш код не поддается расшифровке, и вы вряд ли получите большую помощь