Проблемы с регулярными выражениями
Реализация регулярных выражений в Emacs, как и многие другие, в целом устойчива, но иногда вызывает проблемы двумя способами: сопоставление может исчерпать внутренний стековый объем и сигнализировать об ошибке, и оно может занимать много времени для завершения. Советы ниже сделают эти симптомы менее вероятными и помогут смягчить возникающие проблемы.
- Привязывайте регулярные выражения к началу строки, строки или буфера с помощью утверждений нулевой ширины (‘^’ и
\`). Это использует быстрые пути в реализации и может избежать безуспешных попыток сопоставления. Другие утверждения нулевой ширины также могут принести пользу, вызывая преждевременное завершение сопоставления. - Избегайте шаблонов «или» в пользу альтернатив символов: записывайте ‘[ab]’ вместо ‘a\|b’. Обратите внимание, что ‘\s-’ и ‘\sw’ эквивалентны ‘[[:space:]]’ и ‘[[:word:]]’ соответственно.
- Поскольку последний ветвь шаблона «или» не добавляет точку отката в стеке, рассмотрите возможность размещения наиболее вероятного шаблона сопоставления последним. Например, ‘^\(?:a\|.b\)*c’ исчерпает стек при попытке сопоставления очень длинной строки ‘a’, но эквивалентное ‘^\(?:.b\|a\)*c’ этого не сделает.
(Это компромисс: успешно сопоставленные шаблоны «или» выполняются быстрее, если наиболее часто сопоставляемый шаблон находится в начале.)
- Старайтесь обеспечить, чтобы любая часть текста могла быть сопоставлена только одним способом. Например, ‘a*a*’ сопоставит тот же набор строк, что и ‘a*’, но первый может сделать это многими способами и, следовательно, вызовет медленное обратное отслеживание, если сопоставление завершится неудачно. Если это возможно, сделайте ветви шаблона «или» взаимно исключающими, чтобы сопоставление не уходило далеко в более чем одну ветвь перед завершением неудачей.
Особую осторожность следует проявлять при работе с вложенными повторениями: они могут легко привести к очень медленному сопоставлению при наличии неоднозначностей. Например, ‘\(?:a*b*\)+c’ будет долго пытаться сопоставить даже умеренно длинную строку из ‘a’ перед завершением неудачей. Эквивалентное ‘\(?:a\|b\)*c’ намного быстрее, а ‘[ab]*c’ еще лучше.
- Не используйте группы захвата, если они не действительно необходимы; то есть используйте ‘\(?:…\)’ вместо ‘\(…\)’ для группирования.
- Рассмотрите использование
rx(см. Rx Notation); оно может автоматически оптимизировать некоторые шаблоны «или» и никогда не будет вводить группы захвата, если явно не запрошено.
Если вы столкнетесь с переполнением стека регулярных выражений, несмотря на соблюдение приведенных выше советов, не бойтесь выполнять сопоставление в нескольких вызовах функций, каждый из которых использует более простое регулярное выражение, где обратное отслеживание может быть легче ограничено.
Copyright © 1990-1996, 1998-2022 Free Software Foundation, Inc.
Licensed under the GNU GPL license.
https://www.gnu.org/software/emacs/manual/html_node/elisp/Regexp-Problems.html