PHP: регулярные выражения

Регулярные выражения - мощный гибкий инструмент для синтаксического анализа текста в соответствии с определенным шаблоном.

С помощью регулярных выражений можно эффективно искать фрагменты текста любой сложности, заменять одни вхождения на другие, проверять правильность ввода и разбивать тексты.

Основа регулярного выражения - шаблон. С его помощью описывается формат нужного фрагмента текста.

Любой шаблон должен быть ограничен символами-ограничителями. В качестве таких символов можно использовать любой не буквенно-цифровой символ кроме '/'. Однако предпочтение отдано символу '/', поскольку он не выполняет никаких специальных функций. Выбранный символ-ограничитель шаблона внутри самого шаблона должен экранироваться.

Пример самого простого регулярного выражения:

'/веб-программирование/' - соответствует строке, в которой есть слово 'веб-программирование'.

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

Метасимволы   \ ^ $ . | ? * + [ ] ( ) { }

\ - символ экранирования.
Пример: '/4\/2=2/' - соответствует строке '4/2=2'. Символ '/', используемый как ограничитель шаблона, был экранирован, после чего он перестал выполнять в данном месте свое специальное значение, а стал означать операцию деления внутри текста строки.

^ - символ начала данных.
$ - символ конца данных.
Пример: '/^веб-программирование$/' - соответствует строке, точно совпадающей со словом 'веб-программирование'. Т.е. с буквы 'в' строка начинается и после 'е' заканчивается.

. - любой символ, кроме перевода строки. Но есть модификатор, при использовании которого перевод строки тоже относится к "любым" символам.
Пример: '/веб.программирование/' - соответствует строке, содержащей 'веб-программирование', или 'веб.программирование', или 'вебoпрограммирование' и т.д.

[] - квадратные скобки определяют символьный класс, т.е. набор символов, с которыми проверяется совпадение. Символы могут быть перечислены по отдельности, или в виде некоторого диапазона символов, обозначенного первым и последним символом, разделенных знаком '-', а также с помощью специальных символов. (см.ниже).
Пример: '/б[иао]м/'- под соответствие попадут только строки, содержащие 'бим', 'бам' или 'бом'.

() - круглые скобки используются для определения области действия и приоритета операций. Фактически выделяют группу в шаблоне, так называемую подмаску или подшаблон.
| - перечисление, логическое 'или'.
Пример: '/gray|grey/' или '/gr(a|e)y/' описывают строку 'gray' или 'grey'.

Квантификаторы - ставятся после символа, символьного класса или группы и определяют, сколько раз предшествующее выражение может встречаться.
? - одно или ноль вхождений.
* - любое количество вхождений, в том числе и ноль.
+ - одно или более вхождений.
Пример: выражение '/(б[иао]м-?)*/' соответствует последовательности вида 'бим-бамбом-бам-бамбим-бом' или пустой строке. Выражение '/(б[иао]м-?)+/' соответствует строке, в которой обязательно должно быть хоть одно слово 'бим', 'бам' или 'бом'.

{} - квантификатор, определяющий точное указанное число вхождений символа, символьного класса или группы.

Представление Число повторений Пример Соответствие
{n} Ровно n раз colou{3}r colouuur
{m,n} От m до n включительно colou{2,4}r colouur, colouuur, colouuuur
m,} Не менее m colou{2,}r colouur, colouuur, colouuuur и т. д.
{,n} Не более n colou{,3}r color, colour, colouur, colouuur

Символьные классы
Набор символов в квадратных скобках '[ ]' именуется символьным классом и позволяет указать интерпретатору регулярных выражений, что на данном месте в строке может стоять один из перечисленных символов. В частности, '[абв]' задаёт возможность появления в тексте одного из трёх указанных символов, а '[1234567890]' задаёт соответствие одной из цифр. Возможно указание диапазонов символов: например, '[А-Яа-я]' соответствует всем буквам русского алфавита, за исключением букв 'Ё' и 'ё'.

Если требуется указать символы, которые не входят в указанный набор, то используют символ '^' внутри квадратных скобок, например '[^0-9]' означает любой символ, кроме цифр.

Специальные символы:

\cx - ctrl + x. На месте x может быть любой символ.
\e - escape.
\f - разрыв страницы.
\n - перевод строки.
\r - возврат каретки.
\t - табуляция.
\b - граница слова.
\B - не граница слова.

\d - цифровой символ.
\D - нецифровой символ.
\s - пробельный симол: пробел, перевод строки, табулятор.
\S - непробельный символ.
\w - Буквенный или цифровой символ или знак подчёркивания.
\W - Любой символ, кроме буквенного или цифрового символа или знака подчёркивания.

Две последние конструкции не соответствуют никаким реальным символам.
\xHH - символ с шестнадцатеричным кодом HH. x - это именно буква икс.
\DDD - символ с восьмеричным кодом DDD. Или ссылка на подгруппу (подмаску).

Важно, что метасимволы не активны внутри классов. Например, '[akm$]' будет соответствовать любому из символов 'a', 'k', 'm' или '$'. Знак '$' является метасимволом (как видно из списка выше), но внутри класса он лишается своей особой природы.

Жадные (greedy) повторения

По умолчанию все квантификаторы, обозначающие повторения символов (группы символов), работают по жадному алгоритму: движок будет пытаться повторить его столько раз, сколько это возможно. Если следующие части шаблона не соответствуют, движок вернется назад и попытается попробовать снова с несколькими повторами символа.

Рассмотрим как это работает на примере.

Пусть дано регулярное выражение '/a[bcd]*b/'. Оно соответствует букве 'a', нулю или более символов из класса '[bcd]', и наконец, заключительной букве 'b'. Теперь представим себе сопоставление этого регулярного выражения строке 'abcbd'. Вот как происходит сравнение поэтапно:

1. a — 'a' соответствует регулярному выражению;
2. abcbd — движок сопоставляет '[bcd]*' на как можно большем числе символов, то есть до конца строки (поскольку все символы соответствуют классу в скобках '[]');
3. Неудача — движок пытается сопоставить последний символ в регулярном выражении — букву 'b', но текущая позиция уже в конце строки, где нет никаких символов, так что он терпит неудачу;
4. abcb — вернулись назад, уменьшили на один символ сопоставление с '[bcd]*';
5. Неудача — пытаемся снова найти 'b', но в конце только 'd';
6. abc — снова возвращаемся назад, теперь '[bcd]*' это только 'bc';
7. abcb — снова ищем последний символ регулярного выражения — 'b'. Теперь он действительно находится на нужной позиции и мы добиваемся успеха.

Итак, был достигнут конец регулярного выражения и сопоставление с ним дало 'abcb'. Этот пример показал, как движок сначала забирается так далеко, как может, и, если не находит соответствия, возвращается назад, снова и снова работая с остатком регулярного выражения. Он будет делать так до тех пор, пока не получит ноль совпадений для '[bcd]*', и, если и тогда не получится совпадения, то заключит, что строка совсем не соответствует шаблону.

Такой подход может оказаться значительной проблемой. Например, часто ожидают, что выражение '/<.*>/' найдёт в тексте теги HTML. Однако, если в тексте есть более одного HTML-тега, то этому выражению соответствует целиком строка, содержащая множество тегов.

<p><i>weblecture.ru</i> — студенческий сайт по курсу <b>веб-программирование</b>.</p>

Эту проблему можно решить несколькими способами:

- Учитывать символы, не соответствующие желаемому образцу '/<[^>]*>/'.
- Определить квантификатор как нежадный (ленивый, англ. lazy) — добавив после него знак вопроса '/<.*?>/'.
- Отключить жадность для всего шаблона, используя модификатор '/<.*>/U' (см. ниже).

Модификаторы

Модификаторы вносят новые правила в обработку регулярного выражения и действуют с момента вхождения и до конца регулярного выражения или противоположного модификатора. Указываются они либо в скобках, например так: '(?Ui)', либо после закрывающего символа ограничителя шаблона '/pattern/Ui'.

i - регистронезависимость.
U - инвертирует жадность.
m - многострочный поиск, т.е. символы '^', '$' означают начало и конец всего текста.
s - символ '.' включает в себя и перевод строки.
x - заставляет игнорировать все неэкранированные пробельные символы, если они не перечислены в символьном классе. Удобно, когда переводом строк и пробелами необходимо навести удобочитаемость.

При использовании модификаторов, можно использовать знак '-' для отключения модификатора.
Пример '(?m-i)' - включает многострочный поиск и отключает регистронезависимый.

Здесь надо сказать, что все модификаторы что-то включают. Или отключают, если указаны с минусом. А вот 'U' инвертирует. Т.е. если была жадность включена, он выключит без всяких минусов.

Утверждения

Утверждения - это проверки символов, идущих до или после текущей позиции сопоставления. Утверждения касательно последующего текста начинаются с '(?=' для положительных утверждений и с '(?!' для отрицающих утверждений.

Утверждения касательно предшествующего текста начинаются с '(?<=' для положительных утверждений и '(?<!' для отрицающих.

Пример: регулярное выражение '/(?<!веб-)программирование/' соответствует любому слову 'программирование', кроме 'веб-программирование'.

Комментарии

Комментарии начинаются с '(?#' и продолжаются до ближайшей закрывающей скобки.


Функции PHP для работы с регулярными выражениями

mixed preg_match( string $pattern, string $subject[, array $&a mp;matches[, int $flags]] ) 
mixed preg_match_all( string $pattern, string $subject[, array $&a mp;matches[, int $flags]] ) 
array preg_split( string $pattern, string $subject[, int $limit [, int $flags]] ) 
mixed preg_replace( mixed $pattern, mixed $replacement, mixed $subject[, int $limit] )
mixed preg_replace_callback( mixed $pattern, callback $callback , mixed $subject[, int $limit] ) 

preg_match_all

preg_match_all - выполняет глобальный поиск совпадения регулярного выражения.

Синтаксис: int preg_match_all (string pattern, string subject, array matches [, int flags])

Ищет в subject все совпадения с регулярным выражением pattern и помещает их в matches в порядке, специфицированном в order.

После нахождения первого совпадения последующий поиск продолжается до нахождения последнего совпадения.
flags может принимать следующие значения:

PREG_PATTERN_ORDER (по умолчанию)
Упорядочивает результаты таким образом, что $matches[0] это массив полных совпадений с шаблоном, $matches[1] это массив строк, совпавших с первой подмаской в скобках, и так далее.

preg_match_all ("|<[^>]+>(.*)</[^>]+>|U", 
    "<b>example: </b><div align=left>this is a test</div>", 
    $out, PREG_PATTERN_ORDER);
print $out[0][0].", ".$out[0][1]."\n";
print $out[1][0].", ".$out[1][1]."\n";

Этот пример выдаст:

<b>example: </b>, <div align=left>this is a test</div>
example: , this is a test

Итак, $out[0] содержит массив строк, совпавших со всем шаблоном, а $out[1] содержит массив строк, заключённых в тэги.

PREG_SET_ORDER

Упорядочивает результаты таким образом, что $matches[0] это массив первого набора совпадений, $matches[1] это массив второго набора совпадений, и так далее.

preg_match_all ("|<[^>]+>(.*)</[^>]+>|U", 
    "<b>example: </b><div align=left>this is a test</div>", 
    $out, PREG_SET_ORDER);
print $out[0][0].", ".$out[0][1]."\n";
print $out[1][0].", ".$out[1][1]."\n";

Этот пример выдаст:

<b>example: </b>, example: 
<div align=left>this is a test</div>, this is a test

В данном случае $matches[0] это первый набор совпадений, а $matches[0][0] содержит текст, совпавший с полным шаблоном, $matches[0][1] содержит текст, совпавший с первой подмаской, и так далее. Аналогично $matches[1] это второй набор совпадений и т.д.

Если никакой флаг упорядочивания не задан, принимается PREG_PATTERN_ORDER.

Сервис проверки регулярных выражений.