Регулярные выражения
S
В случае, если планируется многократно использовать шаблон, имеет смысл потратить немного больше времени на его анализ, чтобы уменьшить время его выполнения. В случае, если данный модификатор используется, проводится дополнительный анализ шаблона. В настоящем это имеет смысл только для "незаякоренных" шаблонов, не начинающихся с какого-либо определенного символа.
U (PCRE_UNGREEDY)
Этот модификатор инвертирует жадность квантификаторов, таким образом они по умолчанию не жадные. Но становятся жадными, если за ними следует символ ?. Такая возможность не совместима с Perl. Его также можно установить с помощью (?U) установки модификатора внутри шаблона или добавив знак вопроса после квантификатора (например, .*?).
Замечание:
В нежадном режиме обычно невозможно совпадение символов превышающих pcre.backtrack_limit.
X (PCRE_EXTRA)
Этот модификатор включает дополнительную функциональность PCRE, которая не совместима с Perl: любой обратный слеш в шаблоне, за которым следует символ, не имеющий специального значения, приводят к ошибке. Это обусловлено тем, что подобные комбинации зарезервированы для дальнейшего развития. По умолчанию же, как и в Perl, слеш со следующим за ним символом без специального значения трактуется как опечатка. На сегодняшний день это все возможности, которые управляются данным модификатором.
J (PCRE_INFO_JCHANGED)
Модификатор (?J) меняет значение локальной опции PCRE_DUPNAMES - подшаблоны могут иметь одинаковые имена. Модификатор J поддерживается с версии PHP 7.2.0.
u (PCRE_UTF8)
Этот модификатор включает дополнительную функциональность PCRE, которая не совместима с Perl: шаблон и целевая строка обрабатываются как UTF-8 строки. Недопустимая целевая строка приводит к тому, что функции preg_* ничего не находят, а неправильный шаблон приводит к ошибке уровня E_WARNING. Пятый и шестой октеты UTF-8 последовательности рассматриваются недопустимыми с PHP 5.3.4 (согласно PCRE 7.3 2007-08-28); ранее они считались допустимыми.
"Жадные" и не "жадные" регулярные выражения.
Жадные (greedy) квантификаторы
В современных регулярных выражениях есть несколько разновидностей замыканий. Stephen Cole Kleene, который ввел это понятие, описал два таких: * и +. Как было описано выше, поведение их "жадное" - они пытаются покрыть все что можно - до конца строки. Но дальше в нашем выражении идет следующий оператор или символ, а мы уже в конце строки. Тут парсер откручивает наш квантификатор обратно по точкам возврата, пока не выполнится условие последующего подвыражения.
Очевидно что подобное поведение легко порождает проблемы с производительностью. Вот время выполнения для нескольких вариантов:
Последний случай с двумя звёздочками на самом деле отрабатывает на порядок медленнее. Это связано с особенностью работы парсера. Как было сказано, выражение "любой символ много раз" выполняется дословно и фактически парсер сначала покрывает этим выражением всю строку, сохраняя на каждом символе точку возврата. Увидев что наше выражение не закончено, парсер возвращается обратно, пока не найдет совпадение. Наличие двух звездочек увеличивает количество точек возврата на порядок, трех - еще на порядок. Легко увидеть что такой путь может "простое выражение" сделать ощутимо медленным.
Есть несколько способов улучшить эффективность:
-
интервал со стоп-символом.
Например, если мы ищем теги от '<' до '>', то можно указать интервал вместо произвольного символа: /<[^>]+>/
Парсер остановится, увидев символ вне диапазона и сразу же сработает последующий литеральный символ '>'.
-
использовать интервал повторений \{ min,max \}
Хорошо работает, если нам известно сколько должно быть символов, например при первичной проверке uid или md5 сигнатур.
Нежадные (non-greedy) или ленивые (lazy) квантификаторы
Такой квантификатор действует наоборот - покрывает минимальный набор символов и расширяет его, если последующие сцепленные выражения не выполняются.
С точки зрения производительности очень хорошо работает для вложенных необязательных выражений, но также как и жадный квантификатор может вызвать существенное замедление, поскольку если в хорошем случае ( когда наше выражение выполняется ) все более-менее быстро, то в плохом случае парсер пытается увеличить покрытие для нежадного квантификатора, пока не дойдет до очевидного конца. Что-бы избежать этого, желательно максимально сузить покрываемые символы интервалом и сразу после него поставить простое выражение - фиксированные символы, например.
У ленивых "звездочек" и "плюсов" есть еще один недостаток - они могут и очень часто покрывают слишком мало символов, если границу не обозначить последующим подвыражением. Например, если вы разбираете слова так: \w+? то можете обнаружить что без последующего литерального символа ( в конце большого выражения ), это комбинация покроет только одну букву и в данном случае эффективнее "жадный" вариант. Также жадные эффективнее если четко известно что следующим будет другой символ \w+ так можно описать слово или параметр, ленивый тут просто менее эффективный.
Поиск и замена с применением регулярных выражений
Регулярные выражения мощнейшее средство для поиска в тексте нужных фрагментов и замены их на другие фрагменты. Этот модуль посвящён: подвыражениям и обратным ссылкам; модификаторам регулярных выражений; объекту RegExp; методам объекта String для работы с регулярными выражениями; "экономному" поиску Подвыражения и обратные ссылки.
Мы узнали, что круглые скобки () применяются для группирования последовательности символов в регулярном выражении в единый фрагмент, который после этого начинает вести себя как один символ, и к нему можно будет применить любой квалификатор. (Отметим, что то же самое касается скобок, в которых заключается языковая конструкция вида <<<последовательность 1>>> <<<последовательность 2>>>.) Такой единый фрагмент носит название подвыражения. Подвыражения имеют две очень полезные особенности: 1. Фрагмент текста, совпадающий с подвыражением, запоминается в оперативной памяти, и впоследствии его можно извлечь для использования в сценарии. (Как это сделать, будет рассказано далее.) 2. Мы можем указать, что в определённом месте регулярного выражения должен присутствовать фрагмент, совпадающий с ранее указанным в том же регулярном выражении подвыражением. Для этого используются так называемые обратные ссылки, которые записываются следующим образом: \<<<порядковый номер подвыражения>>>.
Подвыражения нумеруются в порядке слева направо. // Регулярное выражение, совпадающее со строкой, которая включает: // * последовательность из, как минимум, одной латинской буквы a-z, заключённую // в символы < и >, то есть открывающий HTML-тег (<[a-z]+>). // Эта последовательность заключена в круглые скобки и, таким образом, превращена // в подвыражение; // * последовательность из любого количества любых символов (.*); // * последовательность символов, совпадающая с первым подвыражением и также // заключённая в символы </ и >>, то есть парный закрывающий HTML-тег (<\/\1>). // Для обращения к первому подвыражению использовалась обратная ссылка \1. var r = /<([a-z]+)>.*<\/\1>/; // Проверяем, совпадает ли это регулярное выражение со строкой "<p>абзац</p>". // Есть совпадение ! var f = r.test("<p>абзац</p>"); dcument.write(f); // Результат true // А со строкой "<p>абзац</div>" это регулярное выражение не совпадает f = r.test("&<p>абзац</div>"); dcument.write(f); // Результат false 1