Как для минимального, так и для максимального количества повторений, если следующая часть шаблона при сопоставлении завершается неудачно, начинается повторный анализ повторяемого выражения на предмет того, возможно ли успешное сопоставление всего шаблона при другом количестве повторений. Бывают случаи, когда необходимо изменить описанную логику работы для реализации специфического сопоставления либо оптимизации шаблона (если автор уверен, что других вариантов соответствия нет).
В качестве примера, рассмотрим шаблон \d+foo в применении к строке
123456bar
После того, как \d+ будет сопоставлен с первыми шестью цифрами,
сопоставление «foo» потерпит неудачу. После этого, в соответствие
\d+, будет сопоставлено 5 цифр, после очередной неудачи будет сопоставлено
4 цифры и так далее. В конце концов весь шаблон потерпит неудачу.
Однократные подшаблоны указывают, что если одна часть шаблона была
сопоставлена, её не стоит анализировать повторно. Применимо к приведённому
выше примеру весь шаблон потерпел бы неудачу после первого же
неудачного сопоставления с «foo». Записываются однократные шаблоны
при помощи круглых скобок следующим образом: (?>. Например:
(?>\d+)bar
Этот вид подшаблона предотвращает повторный анализ подшаблона, если сопоставление последующих элементов терпит неудачу. Однако это не мешает повторно анализировать любые другие элементы, в том числе те, которые предшествуют однократному подшаблону.
Говоря другими словами, подшаблоны такого типа соответствуют той части подстроки, которой соответствовала бы одиночная изолированная маска, заякоренная на текущей позиции обрабатываемого текста.
Однократные подшаблоны — незахватывающие. Простые примеры, подобные приведённому, можно охарактеризовать как безусловный захват максимального количества повторений. В то время как \d+ и \d+? корректируются так, чтобы остальные части шаблона так же совпали, (?>\d+) соответствует исключительно максимальной по длине последовательности цифр, даже если это приводит к неудаче при сопоставлении других частей шаблона.
Однократным подшаблонам разрешено включать в себя более сложные конструкции и быть вложенными.
Однократные подшаблоны разрешено указывать совместно с утверждениями, которые
касаются предыдущего текста для описания эффективных сопоставлений
в конце обрабатываемого текста. Рассмотрим простой шаблон
abcd$
в применении к длинному тексту, который не соответствует указанной маске.
Поскольку поиск происходит слева направо, вначале PCRE будет
искать букву «a», и только потом анализировать следующие
записи в шаблоне. Если шаблон указан в виде
^.*abcd$
.
В этой ситуации вначале .* сопоставляется со всей строкой, после
чего сопоставление терпит неудачу (так как нет последующего символа «a»).
После чего .* сопоставляется со всей строкой, кроме последнего символа,
потом кроме двух последних символов, и так далее. В конечном итоге
поиск символа «a» происходит по всей строке. Однако, если шаблон записать
в виде:
^(?>.*)(?<=abcd)
повторный анализ для .* не выполняется, и, как следствие, может
соответствовать только всей строке целиком. После чего утверждение
проверяет последние четыре символа на совпадение с «abcd», и в случае
неудачи все сопоставление терпит неудачу. Для больших объёмов
обрабатываемого текста этот подход имеет значительный выигрыш во времени
выполнения.
Если шаблон содержит неограниченное повторение внутри подшаблона,
который в свою очередь также может повторяться неограниченное количество
раз, однократные подшаблоны помогают
избегать многократных неудачных сопоставлений,
которые длятся достаточно продолжительное время. Шаблон
(\D+|<\d+>)*[!?]
соответствует неограниченному количеству подстрок, которые состоят
не из цифр, либо из цифр заключённых в <>, за которыми следует
? либо !. Если в обрабатываемом тексте содержатся
соответствия, время работы регулярного выражения будет невелико.
Но если его применить к строке
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
это займёт длительное время. Это связанно с тем, что строка
может быть разделена между двумя частями шаблона многими способами,
и все они будут опробованы (в примере мы использовали [?!], поскольку
в случае одиночного символа в конце шаблона и PCRE и Perl выполняют
оптимизацию. Они запоминают последний одиночный символ и в случае
его отсутствия выдают неудачу). Если изменить шаблон на
((?>\D+)|<\d+>)*[!?]
,
не цифровые последовательности не могут быть разорваны, и
невозможность сопоставления обнаруживается гораздо быстрее.