Фунции регулярных выражений (perl совместимые)


Содержание

Шпаргалка по регулярным выражениям

Квантификаторы

Аналог Пример Описание
? a? одно или ноль вхождений «а»
+ a+ одно или более вхождений «а»
* a* ноль или более вхождений «а»

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

Символ «минус» (-) меред модификатором (за исключением U) создаёт его отрицание.

Описание
g глобальный поиск (обрабатываются все совпадения с шаблоном поиска)
i игнорировать регистр
m многострочный поиск. Поясню: по умолчанию текст это одна строка, с модификатором есть отдельные строки, а значит ^ — начало строки в тексте, $ — конец строки в тексте.
s текст воспринимается как одна строка, спец символ «точка» (.) будет вкючать и перевод строки
u используется кодировка UTF-8
U инвертировать жадность
x игнорировать все неэкранированные пробельные и перечисленные в классе символы

Спецсимволы

Аналог Описание
() подмаска, вложенное выражение
[] групповой символ
количество вхождений от «a» до «b»
| логическое «или», в случае с односимвольными альтернативами используйте []
\ экранирование спец символа
. любой сивол, кроме перевода строки
\d [0-9] десятичная цифра
\D [^\d] любой символ, кроме десятичной цифры
\f конец (разрыв) страницы
\n перевод строки
\pL буква в кодировке UTF-8 при использовании модификатора u
\r возврат каретки
\s [ \t\v\r\n\f] пробельный символ
\S [^\s] любой символ, кроме промельного
\t табуляция
\w [0-9a-z_] любая цифра, буква или знак подчеркивания
\W [^\w] любой символ, кроме цифры, буквы или знака подчеркивания
\v вертикальная табуляция

Спецсимволы внутри символьного класса

Пример Описание
^ [^da] отрицание, любой символ кроме «d» или «a»
[a-z] интервал, любой симво от «a» до «z»

Позиция внутри строки

Пример Соответствие Описание
^ ^a aaa aaa начало строки
$ a$ aaa aaa конец строки
\A \Aa aaa aaa
aaa aaa
начало текста
\z a\z aaa aaa
aaa aaa
конец текста
\b a\b
\ba
aaa aaa
aaa aaa
граница слова, утверждение: предыдущий символ словесный, а следующий — нет, либо наоборот
\B \Ba\B aaa aaa отсутствие границы слова
\G \Ga aaa aaa Предыдущий успешный поиск, поиск остановился на 4-й позиции — там, где не нашлось a

Скачать в PDF, PNG.

Якоря

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

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

Символьные классы

Символьные классы в регулярных выражениях соответствуют сразу некоторому набору символов. Например, \d соответствует любой цифре от 0 до 9 включительно, \w соответствует буквам и цифрам, а \W — всем символам, кроме букв и цифр. Шаблон, идентифицирующий буквы, цифры и пробел, выглядит так:

POSIX

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

Утверждения

Поначалу практически у всех возникают трудности с пониманием утверждений, однако познакомившись с ними ближе, вы будете использовать их довольно часто. Утверждения предоставляют способ сказать: «я хочу найти в этом документе каждое слово, включающее букву “q”, за которой не следует “werty”».

Приведенный выше код начинается с поиска любых символов, кроме пробела ( [^\s]* ), за которыми следует q . Затем парсер достигает «смотрящего вперед» утверждения. Это автоматически делает предшествующий элемент (символ, группу или символьный класс) условным — он будет соответствовать шаблону, только если утверждение верно. В нашем случае, утверждение является отрицательным ( ?! ), т. е. оно будет верным, если то, что в нем ищется, не будет найдено.

Итак, парсер проверяет несколько следующих символов по предложенному шаблону ( werty ). Если они найдены, то утверждение ложно, а значит символ q будет «проигнорирован», т. е. не будет соответствовать шаблону. Если же werty не найдено, то утверждение верно, и с q все в порядке. Затем продолжается поиск любых символов, кроме пробела ( [^\s]* ).

Кванторы

Кванторы позволяют определить часть шаблона, которая должна повторяться несколько раз подряд. Например, если вы хотите выяснить, содержит ли документ строку из от 10 до 20 (включительно) букв «a», то можно использовать этот шаблон:

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

Этот шаблон соответствует тексту, заключенному в двойные кавычки. Однако, ваша исходная строка может быть вроде этой:

Приведенный выше шаблон найдет в этой строке вот такую подстроку:

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

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

Экранирование в регулярных выражениях

Регулярные выражения используют некоторые символы для обозначения различных частей шаблона. Однако, возникает проблема, если вам нужно найти один из таких символов в строке, как обычный символ. Точка, к примеру, в регулярном выражении обозначает «любой символ, кроме переноса строки». Если вам нужно найти точку в строке, вы не можете просто использовать « . » в качестве шаблона — это приведет к нахождению практически всего. Итак, вам необходимо сообщить парсеру, что эта точка должна считаться обычной точкой, а не «любым символом». Это делается с помощью знака экранирования.

Знак экранирования, предшествующий символу вроде точки, заставляет парсер игнорировать его функцию и считать обычным символом. Есть несколько символов, требующих такого экранирования в большинстве шаблонов и языков. Вы можете найти их в правом нижнем углу шпаргалки («Мета-символы»).

Шаблон для нахождения точки таков:

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

Спецсимволы экранирования в регулярных выражениях

Выражение Соответствие
\ не соответствует ничему, только экранирует следующий за ним символ. Это нужно, если вы хотите ввести метасимволы !$()*+.<>?[\]^ <|>в качестве их буквальных значений.
\Q не соответствует ничему, только экранирует все символы вплоть до \E
\E не соответствует ничему, только прекращает экранирование, начатое \Q

Подстановка строк

Подстановка строк подробно описана в следующем параграфе «Группы и диапазоны», однако здесь следует упомянуть о существовании «пассивных» групп. Это группы, игнорируемые при подстановке, что очень полезно, если вы хотите использовать в шаблоне условие «или», но не хотите, чтобы эта группа принимала участие в подстановке.

Группы и диапазоны

Группы и диапазоны очень-очень полезны. Вероятно, проще будет начать с диапазонов. Они позволяют указать набор подходящих символов. Например, чтобы проверить, содержит ли строка шестнадцатеричные цифры (от 0 до 9 и от A до F), следует использовать такой диапазон:

Чтобы проверить обратное, используйте отрицательный диапазон, который в нашем случае подходит под любой символ, кроме цифр от 0 до 9 и букв от A до F:

Группы наиболее часто применяются, когда в шаблоне необходимо условие «или»; когда нужно сослаться на часть шаблона из другой его части; а также при подстановке строк.

Использовать «или» очень просто: следующий шаблон ищет «ab» или «bc»:

Если в регулярном выражении необходимо сослаться на какую-то из предшествующих групп, следует использовать \n , где вместо n подставить номер нужной группы. Вам может понадобиться шаблон, соответствующий буквам «aaa» или «bbb», за которыми следует число, а затем те же три буквы. Такой шаблон реализуется с помощью групп:

Первая часть шаблона ищет «aaa» или «bbb», объединяя найденные буквы в группу. За этим следует поиск одной или более цифр ( [0-9]+ ), и наконец \1 . Последняя часть шаблона ссылается на первую группу и ищет то же самое. Она ищет совпадение с текстом, уже найденным первой частью шаблона, а не соответствующее ему. Таким образом, «aaa123bbb» не будет удовлетворять вышеприведенному шаблону, так как \1 будет искать «aaa» после числа.

Одним из наиболее полезных инструментов в регулярных выражениях является подстановка строк. При замене текста можно сослаться на найденную группу, используя $n . Скажем, вы хотите выделить в тексте все слова «wish» жирным начертанием. Для этого вам следует использовать функцию замены по регулярному выражению, которая может выглядеть так:

Первым параметром будет примерно такой шаблон (возможно вам понадобятся несколько дополнительных символов для этой конкретной функции):

Он найдет любые вхождения слова «wish» вместе с предыдущим и следующим символами, если только это не буквы или цифры. Тогда ваша подстановка может быть такой:

Ею будет заменена вся найденная по шаблону строка. Мы начинаем замену с первого найденного символа (который не буква и не цифра), отмечая его $1 . Без этого мы бы просто удалили этот символ из текста. То же касается конца подстановки ( $3 ). В середину мы добавили HTML тег для жирного начертания (разумеется, вместо него вы можете использовать CSS или ), выделив им вторую группу, найденную по шаблону ( $2 ).

Модификаторы шаблонов

Модификаторы шаблонов используются в нескольких языках, в частности, в Perl. Они позволяют изменить работу парсера. Например, модификатор i заставляет парсер игнорировать регистры.

Регулярные выражения в Perl обрамляются одним и тем же символом в начале и в конце. Это может быть любой символ (чаще используется «/»), и выглядит все таким образом:

Модификаторы добавляются в конец этой строки, вот так:

Мета-символы

Наконец, последняя часть таблицы содержит мета-символы. Это символы, имеющие специальное значение в регулярных выражениях. Так что если вы хотите использовать один из них как обычный символ, то его необходимо экранировать. Для проверки наличия скобки в тексте, используется такой шаблон:

Шпаргалка представляет собой общее руководство по шаблонам регулярных выражений без учета специфики какого-либо языка. Она представлена в виде таблицы, помещающейся на одном печатном листе формата A4. Создана под лицензией Creative Commons на базе шпаргалки, автором которой является Dave Child. Скачать в PDF, PNG.

Краткое описание регулярных выражений: POSIX и PCRE

Часть 1: Регулярные выражения

Начну с того, что php поддерживает два стандарта регулярных выражений: POSIX и, начиная с четвертой версии, совместимые с Perl. Первый стандарт используется и сервером Apache в mod_rewrite а так же… MySQL в своих запросах (поищите слово «REGEXP» в руководстве по mysql, может сразу поймете, а я об этом позже расскажу). Второй, как ясно из названия, используется в системе perl. Два этих стандарта различаются несильно — во втором есть специальные символы, заменяющие наиболее часто используемые классы символов (например, цифры — d, а буквы и цифры — w) и специальные параметры шаблонов, позволяющие определять регистрозависимость поиска, привязку к концам строк и т.д (в функциях стандарта POSIX регистрозависимость реализована просто: есть функции ereg и ereg_eeplace, есть eregi (insensitive) и eregi_replace). В остальном же оба стандарта совместимы, а приемы написания шаблонов одинаковые.

Если вы работали с Norton/Volkov/Windows Commander или Far, то знаете такую вещь как wildcards. Например: delete c:windows*.* удаляет все файлы из указанной директории. �� В именах файлов особых изощрений делать не приходится, поэтому система простая: символ * означает любой набор символов, в том числе пустой (*.txt), символ ? — любой символ или никакого символа (document?.txt) и еще какие-то обозначения для букв и цифр (я, честно говоря, ими давно не пользовался, поэтому так не вспомню).

В регулярных выражениях подход иной. Система в первую очередь универсальна и должна уметь находить соответствия строк любым самым сложным запросам (странно, что я говорю «должна уметь», система ведь уже «умеет». Надеюсь читатель простит мне такие фразы, ведь они все относятся к уже работающей системе регулярных выражений.) Сейчас я назову термины, которые буду употреблять в дальнейшем, чтобы избежать растяжимых (в прямом и переносном смысле) определений.

Итак, задача системы — помимо четко заданных символов («Вася(.*)Пупкин») позволить указать пользователю поиск заданного количества заданных символов. В приведенном примере с Васей Пупкиным между словами задано любое количество любых символов. Если надо найти шесть цифр, то пишем «[0-9]<6>» (если, например, от шести до восьми цифр, тогда «[0-9]<6,8>»). К чему это все? К тому, что в отличие от wildcard из операционной системы, здесь разделены такие вещи как указатель набора символов и указатель необходимого количества: Вместо набора символов может быть использовано обозначение любого символа — точка, может быть указан конкретный набор символов (поддерживаются последовательности — упоминавшиеся «0-9»). Может быть указано «кроме данного набора символов».

Указатель количества символов в официальной документации по php называется «квантификатор». Термин удобный и не несет в себе кривотолков. Итак, квантификатор может иметь как конкретное значение — либо одно фиксированное («<6>»), либо как числовой промежуток («<6,8>»), так и абстрактное «любое число, в т.ч. 0» («*»), «любое натуральное число» — от 1 до бесконечности («+»: «document[0-9]+.txt»), «либо 0, либо 1» («?»). По умолчанию квантификатор для данного набора символов равен единице («document[0-9].txt»).

Разумеется, для более гибкого поиска сочетаний эти связки «набор символов — квантификатор» можно объединять в метаструктуры.

Как всякий гибкий инструмент, регулярные выражения гибки, но не абсолютно: зона их применения ограничена. Например, если вам надо заменить в тексте одну фиксированную строку на другую, фиксированную опять же, пользуйтесь str_replace. Разработчики php слезно умоляют не пользоваться ради этого сложными функциями ereg_replace или preg_replace, ведь при их вызове происходит процесс интерпретации строки, а это серьезно потребляет ресурсы системы. К сожалению, это любимые грабли начинающих php-программистов (даже я сам, волею судьбы, сперва увидел в руководстве функцию ereg_replace, а только потом, позже, str_replace).

Пользуйтесь функциями регулярных выражений только если вы не знаете точно, какая «там» строка. Из примеров: поисковый код этого сайта, в котором из строки поиска вырезаются служебные символы и короткие слова а так же вырезаются лишние пробелы (вернее, все пробелы сжимаются: » +» заменяется на один пробел). При помощи этих функций я проверяю email пользователя, оставляющего свой отзыв. Много полезного можно сделать, но важно иметь в виду: регулярные выражения не всесильны. Например, сложную замену в большом тексте ими лучше не делать. Ведь, к примеру, комбинация «(.*)» в программном плане означает перебор всех символов текста. А если шаблон не привязан к началу или концу строки, то и сам шаблон «двигается» программой через весь текст, и получается двойной перебор, вернее перебор в квадрате. Нетрудно догадаться, что еще одна комбинация «(.*)» означает перебор в кубе, и так далее. Возведите в третью степень, скажем, 5 килобайт текста. Получается 125 000 000 000 (прописью: сто двадцать пять миллиардов операций). Конечно же, если подходить строго, там стольких операций не будет, а будет раза в четыре-восемь меньше, но важен сам порядок цифр.

Итак, принципы, достоинства и недостатки описаны, теперь надо переходить к конкретике. Два (возможно, сразу следующих) выпуска будут посвящены двум стандартам регулярных выражений — POSIX и PCRE. Описание базовых принципов и понятий работы регулярных выражений.

Часть 2: POSIX

Продолжаем наш разговор. Предыдущий выпуск был вводным, теорией. Сегодня как бы основная часть рассказа — стандарт POSIX. В следующем выпуске я опишу различия, вернее сказать надстройки стандарта совместимого с perl. Итак, обо всем по порядку.
Набор символов

Особо объяснять ничего не нужно. Разве что следующее: не пользуйтесь классом символов для обозначения всего лишь одного (вместо «[ ]+» вполне сойдет » +»). Не пишите в классе символов точку — это ведь любой символ, тогда другие символы в классе будут просто лишними (а в негативном классе получится отрицание всех символов).

Квантификатор

Это, как я уже писал, указатель количества заданных символов. Квантификатором можно указать как конкретное значение, так и пределы. Если число заданных подпадает под пределы квантификатора, фрагмент выражения считается совпавшим с разбираемой строкой. Синтаксис: < >либо

Если нужно указать только необходимый минимум, а максимума нет, просто ставим запятую и не пишем второе число: «<5,>» («минимум 5»). Для наиболее часто употребляемых квантификаторов есть специальные обозначения:

Эти символы должны стоять соответственно в самом начале и в самом конце строки. Чтобы интерпретатор корректно понял символ $ в конце, желательно добавить к нему обратный слэш: ereg(«foo$», $bar)

Структура

Сейчас будет сложное описание, мне оно и самому не нравится. Эта вещь необходима для сложных запросов. Например, вам надо, чтобы в тексте были либо только маленькие буквы, либо только большие, либо только цифры. Класс символов «[a-zA-Z0-9]» не подходит. Тогда пишем такое:

Вертикальная черта — знак «или» регулярных выражений (знака «и», естественно, не существует — это и есть само регулярное выражение). Разделенные вертикальной чертой шаблоны в официальной документации называются альтернативными ветвями (это подразумевает ветвление, т.е. наличие вложенных альтернативных ветвей). Программа сравнивает со строкой все ветви (проходясь по их ряду слева направо), до первого совпадения (это важно учесть, если у вас сложное выражение со вложенными ветвями). Для разделения уровней и отделения этого дерева альтернатив от остального шаблона используются обычные скобки. Если те же большие/маленькие буквы/цифры надо искать внутри контейнера тегов:

Из сложного это, кажется, все. Теперь о более простом. Скобки по-научному называются subpattern (вложенный шаблон). И используются не только для сложных вариантов шаблонов, но и для гибкой замены фрагментов текста или получения их в переменную. К примеру, для печатной версии текста дублируем адреса ссылок текстом в скобках:

Первые скобки — первый вложенный шаблон — можно получить «на выходе» через обозначение «n» (поскольку обратный слэш в php и многих других языках используется для спецсимволов, надо поставить перед ним еще один такой же, чтобы прогамма понимала его буквально). Под нулевым номером — вся совпавшая строка. У себя в печатной версии статьи я не пишу ссылки сразу в тексте, а делаю их список в конце примерно так:

Функция ereg (и eregi), если ей указать в третьем параметре переменную, то туда будут записаны все подстроки в виде двухмерного массива.

Это, собственно, все. Дальше нужно только уметь составлять шаблоны. Приведу несколько примеров.

* Переписывание адресов сервером Apache (как я уже отметил, Apache работает со стандартом POSIX).
* Поиск по базе данных: из пользовательского поискового запроса делается sql-запрос. Если отбросить создание статистики поиска (сколько найдено всего, сколько по каждому слову), то получится, что необходимо всего 6-7 строк кода. Там же описана и подсветка слов в результатах поиска. Кстати, важное замечание: перед тем, как вырезать короткие слова из строки я заменяю пробелы между словами на двойные. Почему? Потому что совпадающие с шаблоном строки не должны наезжать друг на друга.

Объясню подробнее. Если в шаблоне нет никаких якорей, система проходится по тексту слева направо, и если найдено совпадение, кидает его в какие-то переменные, а затем перескакивает на следующий символ после совпавшего фрагмента. Мы ищем по шаблону «пробел, два непробела, пробел», а пробелы одиночные. Программа находит «пробел-короткое слово-пробел», заменяет это на один пробел, а затем перескакивает на… первую букву следующего слова. Это не пробел, поэтому даже если следующее слово тоже короткое, оно под шаблон не подойдет. Поэтому-то и надо предварительно заменить пробелы на двойные.
* Как хранить новости в файлах и не бегать циклом по дате:

4. Проверка правильного написания email-а:

На этом все. В следующем выпуске — стандарт PCRE, точнее дополнительные возможности, которые он предоставляет.

Часть 3: PCRE

И вот, наконец, серия выпусков про регулярные выражения подходит к концу. Поговорим о регулярных выражениях совместимых с Perl (Perl compatible regular expressions — PCRE).

Самое главное их преимущество перед POSIX, как мне уже подсказывают — возможность «жадного» поиска. Вопросительный знак в PCRE выступает еще и как минимизатор квантификатора: .*?Найдет минимальную подходящую строку. Вроде бы ничего особенного? Нет, это очень особенная вещь. Например, какой пример я приводил в прошлом выпуске про печатную версию текста?

То есть, внури ссылки не должно быть тегов (например ««). Если же сделать так:

Тогда мы получим… Правильно, весь текст между началом первой и концом последней ссылки. Все проблемы снимает жадный поиск.

Программа подберет для всех ссылок минимальную подходящую строку, т.е. только до тега «». Описывать значение такой особенности PCRE нет смыла — оно огромное. �� Идем дальше.

Цифры теперь можно обозначить не как «[0-9]», а просто «d». Не-цифры
(«[^0-9]») как «D». Очень удобно. Вот остальные обозначения:

Рекомендую заглянуть в выпуски про поиск — там эти символы используются.

Строка шаблона, как вы уже заметили, начинается и заканчивается слэшами. Для чего нужен первый слэш, не знаю. Последний нужен для отделения шаблона от параметров. Параметры, которые я понял, таковы:
i регистронезависимый поиск
m многостроковый режим. По умолчанию PCRE ищет сопвадения с шаблоном только внутри одной строки, а символы «^» и «$» совпадают только с началом и концом всего текста. Когда этот параметр установлен, «^» и «$» совпадают с началом и концом отдельных строк.
s символ «.» (точка) совпадает и с переносом строки (по умолчанию — нет)
A привязка к началу текста
E заставляет символ «$» совпадать только с концом текста. Игнорируется, если установлен парамерт m.
U Инвертирует «жадность» для каждого квантификатора (если же после квантификатора стоит «?», этот квантификатор перестает быть «жадным»).

Естественно, регистр в параметрах имеет значение. Остальное о них можно прочесть в руководстве по php.

Теперь о функциях PCRE.

Функция preg_match в отличие от ereg ищет только первое совпадение. Если нужно найти все совпадения и как-то обработать их результаты (но не напрямую через preg_replace), нужно пользоваться preg_match_all. Параметры этой функции те же.

Из полезного отмечу функцию preg_quote, которая вставляет слэши перед всеми служебными символами (например, скобками, квадратными скобками и т.п.), чтобы те воспринимались буквально. Если у вас есть какой-либо ввод информации пользователем, и вы проверяете его через PCRE, лучше перед этим закомментировать служебные символы в пришедшей переменной (мало ли что он там напишет, это ведь по определению злобный хакер).

Это все, что я могу сказать про регулярные выражения. Дальше — только искусство комбинирования строк и написания алгоритмов.

Помнится, в одном из пришлых выпусков я описал рассыльщик почты на классах. Теперь я добавил туда хранение адресов в файлах и подтверждение подписки. Разумеется, различные проверки адресов, получение списка активных и тому подобное — все работает на PCRE. К сожалению, времени на тестирование и доводку не было, рассыльщик «сырой».

Фунции регулярных выражений (perl совместимые)

В данной статье я попытаюсь как можно лучше объяснить что такое регулярные выражения в Perl. Изюминкой Perl является работа с текстом — для этого он и был создан. Регулярные выражения очень часто используются в unix системах, например для поиска файлов по шаблону, также в sh, примеров много и всех не перечислить, Perl не исключение.

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

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

Начнем с того, что в Perl имеются три основных оператора для работы с текстом:

также парочка полезных функций:

m/berry/i) <
print «This is berry string !»;
>

В данном случае будет выведена строка «This is berry string !». Для сравнения скаляра с шаблоном нужно использовать сравнение такого вида «=

«. Здесь операратор m/. / вернет истину или ложь(т.к. данное регулярное выражение используется в скалярном контексте, а не в скписковом — о нем ниже), если его опустить, то оператор m/. / будет использовать специальную переменную $_.

Буква i после второй косой черты значит игнорированние регистра, как вы видите в шаблоне написано «berry» а в $string — «Berry», т.е. если бы не было буквы i, то наша программа не вывела бы строку «This is berry string !». Шаблон m/berry/i совпадет со строками «BERRY», «berry», «BeRrY» и т.д.

Данная программа открывает файл и идет по строкам (по строкам т.к. по умолчанию переменная $/ является переносом строки, переопределяя ее — вы меняете терминатор строки), сохраняя каждую строчку в перменную $lines, а если в строчке встречается слово «exit» — то выходит из цикла.

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

Также поменять смысл на противоположный можно просто сменив оператор «=

/berry/i) <
print «This is NOT berry string!»;
>else <
print «This is berry string!»;
>

В данном случае будет выведена строка «This is berry string !». Но если нам немного поменять строку, таким образом:

Логически мысля, человек ищет строчку berry (с англ. ягода), но у нас в строчке strawberry (с англ. клубника), т.е. это нам не подходит, хотя наш шаблон все равно сработает и выведет «This is berry string !», хотя должен вывести «This is NOT berry string!».

Это получается потому что, когда оператор получает шаблон «berry», и другую строку, в которой он ищет, символ «b», за которым следует «e», затем «r», «r» и «y», а все что находится до этой последовательности или после нее, не имеет значение.

Поэтому придется улучшить шаблон, подставив мнимый символ (об этих символах ниже), вот так:

/\bberry\b/i) <
print «This is NOT berry string!»;
>else <
print «This is berry string !»;
>

А вот сейчас все правильно — теперь наша программа выведет «This is NOT berry string!». «\b» в начале шаблона это мнимый символ соответсвующий границе слова. Как вы видите некоторые символы имеют определенный смысл для регулярных выражений. Метасимволы могут создавать альтернативные значения, организовывать повоторы, группировать (что позволяет запоминать часть найденной строки), создавать классы символов и т.д. Все метасимволы начинаются с обратной косой черты (\). Если шаблон содержит символы косой черты (например анализ каталогов, или HTML тег), то лучше использовать другие ограничители, т.к. перед каждой чертой придется ставить обратную косую черту (\).

Как видите перед каждой косой чертой пришлось ставить обратную косую черту — некрасиво и очень громоздко, поэтому, заменим разделитель (символ ? и ‘ лучше не использовать в качестве разделителей, т.к. шаблоны ограниченные этими символами обрабатываются иначе):

Документация > PHP > Синтаксис шаблонов (официальная документация)

Библиотека PCRE является набором функций, которые реализуют поиск по шаблону, используя синтаксис, подобный синтаксису Perl 5 с небольшими отличиями.

Отличия от Perl

Разница описана относительно версии Perl 5.005.

  1. По умолчанию пробельными символами являются все символы, распознаваемые библиотечной функцией языка Си isspace(). Это позволяет собрать PCRE библиотеку с произвольными символьными наборами. В стандартной поставке функция isspace() определяет как пробельные следующие символы: пробел, разрыв страницы, начло строки, перевод каретки, горизонтальную и вертикальную табуляцию. Начиная с версии Perl 5.002, символ вертикальной табуляции \v не является пробельным и, соответственно, не соответствует классу символов \s.
  2. PCRE не позволяет использовать квантификаторы повторения в условных выражениях. Perl позволяет это делать, но получаемый результат может существенно отличаться от ожидаемого. Например, (?!a) <3>не означает, что три следующих символа будут не ‘a’. Он всего лишь трижды утверждает, что следующий символ не ‘a’.
  3. Во время сопоставления подмаски, находящейся внутри отрицающего условного выражения, счетчик подмасок увеличивается, но сами значения, зафиксированные такой подмаской, не возвращаются (в результирующем массиве по соответствующим смещениям находятся пустые строки). Perl устанавливает значения соответствующих числовых переменных исходя из предшествующей модмаски, найденной непосредственно перед тем, как отрицающее условие не смогло быть сопоставлено (и таким образом выполнилось), но только в том случае, если условное выражение содержит только одну ветвь.
  4. Несмотря на то, что символ, соответствующий ASCII-коду 0 (бинарный ноль), допускается в обрабатываемом тексте, он недопустим в шаблоне (так как передается в качестве аргумента Си-функции как нормальная строка, завершаемая нулевым символом). Cледующая служебная последовательность «\\x00» может быть использована для представления бинарного ноля.
  5. Следующие служебные последовательности, используемые в Perl, не поддерживаются: \l, \u, \L, \U, \E, \Q. Это связано с тем, что обработка указанных последовательностей производится внутренним Perl-механизмом обработки строк и не является частью механизма регулярных выражений.
  6. Perl модификатор \G не поддерживается, так как он не входит в рамки простого сопоставления шаблону.
  7. Достаточно очевидно, что PCRE не поддерживает конструкции вида (?).
  8. Теперь немного о чудаковатости в Perl 5.005_02, связанной с фиксацией результата в случае, когда часть шаблона повторяется. Например, применяя шаблон /^(a(b)?)+$/ к строке "aba", переменная $2 соответствует 'b'. Но при применении шаблона /^(aa(bb)?)+$/ к строке "aabbaa" переменная $2 оказывается неустановленной. А в случае, если шаблон изменить на /^(aa(b(b))?)+$/, переменные $2 и $3 окажутся установленными. В Perl 5.004, в обоих случаях переменная $2 будет содержать соответствующее значение, что соответствует PCRE. Если в будущем Perl изменит поведение в данной ситуации, PCRE также может измениться.
  9. Еще одна несовместимость, не находящая разумного объяснения. Шаблон /^(a)?(?(1)a|b)+$/ соответствует строке 'a' в PERL, но не в PCRE. В то же время шаблон /^(a)?a/ соответствует строке 'a' и в Perl и в PCRE, оставляя переменную $1 неустановленной.
  10. PCRE также предоставляет некоторое расширение возможностей Perl для обработки регулярных выражений:

Несмотря на то, что условное выражение, ссылающееся на предыдущие вхождения, должно соответствовать строке фиксированной длины, каждая ветка такого выражения в отдельности может соответствовать строке произвольной длины (отличающейся от длины других веток). В то время как Perl 5.005 требует, чтобы все они имели одинаковую длину.

В случае, если модификатор PCRE_DOLLAR_ENDONLY используется и PCRE_MULTILINE не используется, специальный символ '$' соответствует исключительно концу обрабатываемых данных.

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

Модификатор PCRE_UNGREEDY инвертирует жадность квантификаторов, таким образом они по умолчанию не жадные. Но становятся жадными, если за ними следует символ '?'.

Регулярные выражения в деталях

Предисловие

Ниже описан синтаксис Perl-совместимых регулярных выражений (PCRE). Регулярные выражения также хорошо описаны в документации языка Perl и в достаточно большом количестве книг, с изобилием примеров, например, книга "Mastering Regular Expressions", написанная Effrey Friedl's (ISBN 1-56592-257-3).

Регулярное выражение — это шаблон, применяемый к заданному тексту слева направо. Большая часть символов сохраняет свое значение в шаблоне и означает совпадение с соответствующим символом. Банальный пример: шаблон The quick brown fox соответствует той части строки, которая идентична приведенной фразе.

Метасимволы

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

Существуют два различных набора метасимволов: те, которые используются внутри квадратных скобок, и те, которые используются вне квадратных скобок. Рассмотрим их более детально. Вне квадратных скобок используются следующие метасимволы:

\
общий экранирующий символ, допускающий несколько вариантов применения

^
декларирует начало данных (или линии, в многострочном режиме)

$
декларирует конец данных (или линии, в многострочном режиме)

.
соответствует любому символу, кроме перевода строки (по умолчанию)

[
начало описания символьного класса

]
конец описания символьного класса

|
начало ветки условного выбора

?
расширяет смысл метасимвола '(' , квантификатор, означающий ноль либо одно вхождение, квантификатор жадности

*
квантификатор, означающий ноль или более вхождений

+
квантификатор, означающий одно или более вхождений

<
начало количественного квантификатора

>
конец количественного квантификатора

Часть шаблона, заключенная в квадратные скобки, называется символьным классом. Внутри символьных классов используются следующие метасимволы:

\
общий экранирующий символ

^
означает отрицание класса, допустим только в начале класса

-
означает символьный интервал

]
завершает символьный класс

В нижеследующих секциях детально описан каждый из перечисленных метасимволов.

Обратный слеш

Символ '\' имеет несколько применений. Прежде всего, если он предшествует не буквенно-цифровому символу, он снимает с него специальное значение. Применение обратного слеша как экранирующего символа допустимо как в символьном классе, так и вне него.

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

В случае, если указан модификатор PCRE_EXTENDED, пробельные символы в шаблоне (вне описания символьного класса) игнорируются. Также игнорируется часть строки, находящаяся между символом '#' (опять же, не участвующем в описании символьного класса) и следующим символом перевода строки. В таком случае обратный слеш можно применять как экранирующий символ для указания вхождений пробельным символов в шаблоне.

Второе примение обратного слеша заключается в том, что он позволяет использовать непечатные символы в описании шаблона. При том, что в PCRE нет ограничений на использование непечатных символов (исключая бинарный 0, который интерпретируется как конец шаблона), при редактировании программного кода в каком-либо текстовом редакторе гораздо удобнее использовать следующие комбинации:

\a
символ оповещения, сигна, (шестнадцатиричный код 07)

\cx
"Ctrl+x", где x - произвольный символ

\e
escape (шестнадцатеричный код 1B)

\f
разрыв страницы (шестнадцатиричный код 0C)

\n
перевод строки (шестнадцатиричный код 0A)

\r
возврат каретки (шестнадцатиричный код 0D)

\t
табуляция (шестнадцатиричный код 09)

\xhh
символ с шестнадцатиричным кодом hh

\ddd
символ с восьмеричным кодом либо ссылка на подмаску

Если быть более точным, комбинация \cx интерпретируется следующим образом: если 'x' - символ нижнего регистра, он преобразуется в верхний регистр. После этого шестой бит инвертируется. Таким образом '\cz' интерпретируется как 1A, в то время как '\c;' получает шестнадцатиричное значение 3B, а '\c;' - 7B.

После "\x" считываются еще две шестнадцатиричные цифры (они могут быть записаны в нижнем регистре).

После "\0" считываются две восьмеричные цифры. Если в записи менее двух цифр, будут использованы все фактически присутствующие цифры. Таким образом, последовательность "\0\x\07" будет интерпретирована как два бинарных нуля, за которыми следует символ оповещения (звонок). В случае, если вы используете представление числа в восьмеричном коде, убедитесь, что за начальным нулем следуют две значащие цифры.

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

Внутри символьного класса, либо если полученное значение больше 9 и соответствующее количество предшествующих подмасок отсутствует, PCRE считывает до трех восьмеричных цифр, следующих за обратным слешем, и генерирует один байт из последних 8-ми значащих битов полученного значения. Все последующие цифры обозначают себя же. Например:

\040
еще один способ записи пробела


\40
то же самое в случае, если данной записи предшествует менее сорока подмасок

\7
всегда интерпретируется как ссылка на подмаску

\11
может быть как обратной ссылкой, так и альтернативной записью символа табуляции

\011
всегда интерпретируется как символ табуляции

\0113
символ табуляции, за которым следует цифра "3"

\113
интерпретируется как символ с восьмеричным кодом 113 (так как ссылок на подмаски не может быть более чем 99)

\377
байт, всецело состоящий из единичных битов

\81
либо обратная ссылка, либо бинарный ноль, за которым следуют цифры "8" и "1"

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

Все последовательности, определяющие однобайтное значение, могут встречаться как внутри, так и вне символьных классов. Кроме того, внутри символьного класса запись "\b" интерпретируется как символ возврата ('backspace', шестнадцатеричный код 08). Вне символьного класса она имеет другое значение (какое именно, описано ниже).

Третье использование обратного слеша - указание общего типа символов:

\d
любая десятичная цифра

\D
любой символ, кроме десятичной цифры

\s
любой пробельный символ

\S
любой непробельный символ

\w
Любой символ, образующий "слово"

\W
Любой символ, не образующий "слово"

Каждая пара таких специальных последовательностей делит полное множество всех символов на два непересекающихся множества. Любой символ соответствует одному и только одному множеству из пары.

"word" символ - это произвольная цифра, буква или символ подчеркивания, проще говоря, любой символ, который может являться частью 'слова' в Perl. Определение букв и цифр управляется символьными таблицами, с которыми PCRE был собран. И, как следствие, эти наборы могут отличаться в различных локализированных дистрибутивах. Например, в локали "fr" (Франция) некоторые символы с кодом выше 128 используются для записи ударных символов и, соответственно, соответствуют маске \w.

Описанные выше типы символов могут применяться как внутри, так и вне символьных классов, и соответствуют одному символу данного типа.

Четвертое использование обратного слеша - нотация некоторых формальных утверждений, описывающих условия касательно месторасположения особых позиций в строке и совершенно не затрагивающих сами символы. Такими управляющими последовательностями являются:

\B
не является границей слова

\A
начало данных (независимо от многострочного режима)

\Z
конец данных либо позиция перед последним символом строки, в случае если это символ перевода строки (независимо от многострочного режима)

\z
конец данных (независимо от многострочного режима)

Описанные выше последовательности не могут встречаться в символьных классах (исключая комбинацию '\b', которая внутри класса означает символ возврата 'backspace').

Границей слова считается такая позиция в строке, в которой из текущего и последующего символа только один соответствует \w (т.е. один из них соответствует \w, а другой \W). Начало или конец строки также соответствуют границе слова в случае, если первый или, соответственно, последний символ совпадает с \w.

Специальные последовательности \A, \Z и \z отличаются от общеупотребляемых метасимволов начала строки '^' и конца строки '$' тем, что их поведение не зависит от наличия или отсутствия модификаторов. На них никак не влияют опции PCRE_MULTILINE и PCRE_DOLLAR_ENDONLY. Разница между \Z и \Z в том, что \Z соответствует позиции перед последним символом в случае, если последний символ - перевод строки. В то время, как \z соответствует исключительно концу данных.

Метасимволы начала и конца строки

По умолчанию, вне символьного класса метасимвол начала строки '^' соответствует началу обрабатываемых данных (если не используются модификаторы). Внутри символьного класса он имеет совершенно другое значение.

Метасимвол '^' не обязан быть первым символом строки в случае, если в шаблоне используются несколько альтернатив, но должен быть первым символом в каждой из альтернатив, в которой он встречается, если шаблон когда-либо сопоставим с соответствующей веткой. Если все альтернативы начинаются с метасимвола начала строки, то шаблон ограничен для совпадения исключительно в начале строки, говорят что шаблон "заякорен". (Существуют и другие способы "заякорить" шаблон).

Соответствие метасимволу конца строки достигается только в конце строки или непосредственно перед последним символом в случае, если им является перевод строки (если модификаторы не указаны). Метасимвол конца строки не обязан быть последним символом шаблона в случае, если несколько альтернатив используется, но должен быть последним символом в каждой альтернативе, в которой он фигурирует. Внутри символьного класса символ '$' не имеет специального значения.

Поведение метасимвола конца строки может быть изменено при помощи модификатора PCRE_DOLLAR_ENDONLY так, чтобы он соответствовал исключительно концу строки. Данный флаг никак не касается специальной последовательности \Z.

Значение метасимволов начала и конца строки меняется в случае, если модификатор PCRE_MULTILINE используется. В таком случае, помимо совпадений в начале либо конце строки, метасимволы '^' и '$' соответствуют позиции непосредственно после символа перевода строки соответственно. Например, шаблон /^abc$/ встречается в строке def\nabc" в многострочном режиме и не встречается в нормальном режиме. Таким образом, шаблон который "заякорен" в однострочном режиме, не будет являться "заякоренным" в многострочном режиме. Модификатор PCRE_DOLLAR_ENDONLY игнорируется в случае, если модификатор PCRE_MULTILINE установлен.

Следует заметить, что служебные последовательности \A, \Z и \z могут использоваться для сопоставления с началом либо концом строки в обоих режимах. И если все ветви шаблона начинаются с \A, шаблон будет заякорен независимо от присутствия модификатора PCRE_MULTILINE.

Метасимвол точка

Вне символьного класса символ точка соответствует любому (в том числе и непечатному, бинарному) символу, кроме символа перевода строки '\n'. В случае, если модификатор PCRE_DOTALL используется, точка соответствует также символу перевода строки. Обработка метасимвола "точка", никак не связана с метасимволами начала и конца строки, единственное, что у них общего,- так это специальное отношение к символу перевода строки. Внутри символьного класса точка не имеет специального значения.

Квадратные скобки

Открывающая квадратная скобка объявляет начало символьного класса, завершаемого закрывающей квадратной скобкой. Символ ']' не имеет специального значения, и в случае, если закрывающая квадратная скобка необходима как член символьного класса, она должна быть первым символом непосредственно после открывающей квадратной скобки (если используется метасимвол '^', то непосредственно после него), либо экранироваться при помощи обратного слеша.

Символьный класс соответствует одиночному символу обрабатываемой строки, причем сам символ должен содержаться в наборе, определяемым классом. В случае, если первым символом описания класса является '^', логика работы инвертируется: класс соответствует одиночному символу, который не содержится в наборе, определяемым классом. Если символ '^' необходим как член класса, его не следует помещать первым символом в описании класса либо необходимо экранировать при помощи обратного слеша.

К примеру, символьный класс [aeiou] соответствует любой гласной букве в нижнем регистре, в то время, как [^aeiou] соответствует любому символу, не являющемуся гласной буквой нижнего регистра. Следует понимать, что символ '^' всего лишь удобный инструмент для описания символов, не используемых в сопоставлении.

В случае, если производится регистронезависимое сопоставление, любая буква символьного класса соответствует как своему верхнему, так и нижнему регистру. Таким образом символьный класс [aeiou] соответствует как 'A', так и 'a'. Аналогично, класс [^aeiou] не соответствует ни 'A', ни 'a'.

Внутри символьного класса символ перевода строки "\n" не имеет специального значения, независимо от наличия модификаторов PCRE_DOTALL и PCRE_MULTILINE. Символьные классы, построенные на отрицании, например [^a], соответствуют символу перевода строки.

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

Недопустимо использовать закрывающую квадратную скобку в качестве границы символьного диапазона. К примеру шаблон '[W-]46]' будет интерпретирован как символьный класс, состоящий из двух символов ("W" и "-"), за которыми следует строка "46]", таким образом шаблон будет соответствовать строкам "W46]" или "-46]". Чтобы все же использовать символ ']' в описании диапазона, его необходимо экранировать при помощи обратного слеша, к примеру шаблон [W-\]46] будет интерпретирован как символьный класс, состоящий из символьного диапазона вместе с двумя последующими символами '4' и '6'. Такого же результата можно достичь используя шестнадцатиричное или восьмеричное представление символа ']'.

Для построения символьных диапазонов используется ASCII представление символов. Таким образом пограничные символы можно задавать непосредственно в числовом представлении, например, [\000-\037]. В случае, если выполняется регистронезависимый поиск, символы, описанные в диапазоне, также будут соответствовать символам обеих регистров. К примеру, диапазоны [W-c] и [][\^_`wxyzabc] эквивалентны (в случае регистронезависимого поиска). Например, если установлена локаль "fr" (Франция) необходимо использовать [\xc8-\xcb] для задания соответствия ударному 'E' в обоих регистрах.

Общие типы символов \d, \D, \s, \S, \w, и \W также могут использоваться в символьных классах, добавляя при этом в класс те символы, которым соответствуют. Например, класс [\dABCDEF] соответствует любой шестнадцатиричной цифре. Символ '^' может использоваться совместно с общим типом, взятым в верхнем регисте, для указания более узкого набора символов. Например, класс [^\W_] соответствует любой букве или цифре, но не символу подчеркивания.

Все неалфавитные символы, кроме \, -, ^ (вначале) и завершающего ']', не являются специальными символами, но использование экранирующего слеша перед ними не навредит.

Вертикальная черта

Символ вертикальной черты '|' используются для разделения альтернативных масок. Например шаблон gilbert|sullivan соответствует как "gilbert" так и "sullivan". Допустимо указывать любое количество альтернатив, также допустимо указывать пустые альтернативы (соответствуют пустой строке). В процессе поиска соответствия просматриваются все перечисленные альтернативы слева направо, останавливаясь после первого найденного соответствия. В случае, если альтернативные варианты перечислены в подшаблоне, то поиск соответствия означает нахождение соответствия одному из альтернативных вариантов подмаски и остатку основного шаблона.

Установка внутренних опций

Установки PCRE_CASELESS, PCRE_MULTILINE, PCRE_DOTALL и PCRE_EXTENDED могут быть локально предопределены в шаблоне с использованием специальных символьных Perl-последовательностей, заключенных между символами "(?" и ")".

Таблица 1. Символы внутренних опций

i for PCRE_CASELESS
m для PCRE_MULTILINE
s для PCRE_DOTALL
x для PCRE_EXTENDED

Например, (?im) указывает на регистронезависимый, многострочный режим поиска. Также можно сбросить опцию, поставив перед ней символ '-', либо комбинировать установку и отмену режимов. Например, (?im-sx) устанавливает флаги PCRE_CASELESS, PCRE_MULTILINE и отменяет флаги PCRE_DOTALL и PCRE_EXTENDED. В случае, если опциональный символ расположен непосредственно после либо перед символом '-', опция будет отменена.

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

что, в свою очередь равносильно компиляции шаблона 'abs' с указанием модификатора PCRE_CASELESS. Другими словами, такие установки верхнего уровня применяются ко всему шаблону (если отсутствуют дополнительные модификаторы в подмасках). Если присутствуют несколько опций верхнего уровня, будет использована самая правая опция.

В случае, если опция встречается в подмаске, эффект может быть разным. В Perl 5.005 была добавлена следующая особенность: опция внутри подмаски влияет только на ту часть подмаски, которая идет после указания опции. Таким образом (a(?i)b)c соответствует исключительно строкам 'abc' и 'aBc' (предполагается, что модификатор PCRE_CASELESS не используется). Это означает, что cуществует возможность указывать различные наборы опций для отдельных участков шаблона. Применение опций в одной из альтернативных веток также распространяется на все последующие ветки. Например: (a(?i)b|c) соответствует "ab", "aB", "c" и "C", хотя при совпадении с "C" первая ветвь покидается до установки опции. Это объясняется тем, что установка всех опций происходит на этапе компиляции шаблона.

Опции, специфичные для PCRE, такие как PCRE_UNGREEDY и PCRE_EXTRA могут быть установлены точно так же, как и Perl-совместимые опции, путем использования символов U и X соответственно. Флаг (?X) специфичен тем, что должен быть расположен в шаблоне прежде, чем будет использоваться любая другая дополнительная возможность, даже если он расположен на верхнем уровне. Лучше всего размещать флаг (?X) в самом начале шаблона.

Подмаски

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

  1. Локализирует набор альтернатив. Например, шаблон cat(aract|erpillar|) соответствует одному из слов "cat", "cataract" или "caterpillar". Без использования скобок он соответствовал бы строкам "cataract", "erpillar" или пустой строке.
  2. Указывает на необходимость захвата подстроки. В том случае, если соответствие шаблону было найдено, подстроки, соответствующие подмаскам, также передается обратно вызывающему при помощи аргумента ovector функции pcre_exec(). Открывающие круглые скобки нумеруются слева направо начиная с единицы и их порядковые номера используются для нумерации соответствующих подстрок в результате.

Например, если строка "the red king" сопоставляется с шаблоном the ((red|white) (king|queen)), будут захвачены подстроки "red king", "red" и "king", и их номера соответственно 1, 2 и 3.

На самом деле выполнение одновременно двух функций не всегда удобно. Бывают случаи, когда необходима группировка альтернатив без захвата строки. В случае, если после открывающей круглой скобки следует "?:", захват строки не происходит, и текущая подмаска не нумеруется. Например, если строка "the white queen" сопоставляется с шаблоном the ((?:red|white) (king|queen)), будут захвачены подстроки "white queen" и "queen", и они будут пронумерованы 1 и 2 соответственно. Максимальное количество захватывающих подмасок - 99, максимальное количество всех подмасок - 200.

В случае, если в незахватывающей подмаске необходимо указать дополнительные опции, можно воспользоваться удобным сокращением: символ, обозначающий устанавливаемую опцию помещается между "?" и ":". Таким образом, следующие два шаблона

соответствуют одному и тому же набору строк. Поскольку альтернативные версии берутся слева направо, и установленные опции сохраняют свое действие до конца подмаски, опция, установленная в одной ветке, также имеет эффект во всех последующих ветках. Поэтому приведенные выше шаблоны совпадают как с "SUNDAY", так и с "Saturday".

Повторение

Повторение задается при помощи квантификаторов, следующих за любым из указанных ниже элементов:

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

Общий квантификатор повторения указывает минимальное и максимальное допустимое количество совпадений, согласно двум числам, заключенными в фигурные скобки и разделенными запятой. Числа должны быть меньше чем 65536, и первое число не должно превышать второе по значению. Например: z <2,4>соответствует "zz", "zzz" или "zzzz". Закрывающая фигурная скобка сама по себе не является специальным символом. В случае, если второе число опущено, но запятая присутствует, нет верхнего предела; в случае, если и второе число и запятая опущены, требуется точное число повторений. Таким образом [aeiou] <3,>соответствует как минимум трем последовательным гласным (а также любому их количеству выше трех), в то время как \d <8>соответствует исключительно восми цифрами. Открывающая фигурная скобка, расположенная в недопустимой для квантификатора позиции, либо не соответствующая синтаксису квантификатора, интерпретируется как обыкновенная символьная строка. Например, <,6>не является квантификатором, а интерпретируется как символьная строка из четырех символов.

Квантификатор <0>является допустимым и ведет себя таким образом, будто бы сам квантификатор и предшествующий ему элемент отсутствуют.

Для удобства (а так же обратной совместимости) три наиболее распространённых квантификатора имеют односимвольные аббревиатуры:

Таблица 2. Односимвольные квантификаторы

* эквивалентен <0,>
+ эквивалентен <1,>
? эквивалентен

Можно конструировать бесконечные циклы, указав после шаблона, не содержащегося в заданной строке, квантификатор, не имеющий верхнего предела, например: (a?)*

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

По умолчанию, все квантификаторы являются "жадными", это означает, что они совпадают максимально возможное количество раз (но не более, чем максимально допустимое количество раз), не приводя к невозможности сопоставления остальных частей шаблона. Классический пример проблем, которые могут возникнуть в связи с такой особенностью квантификаторов - нахождение комментариев в C-программах. Комментарием считается произвольный текст, находящийся внутри символьных комбинаций /* и */ (при этом, символы '/' и '*' также могут быть частью комментария). Попытка найти комментарии при помощи шаблона /\*.*\*/ в строке /* первый комментарий */ не комментарий /* второй комментарий */ закончится неудачей, поскольку указанный шаблон соответствует всей строке целиком (из-за жадности кватификатора '*').

Однако, если сразу же после квантификатора идет вопросительный знак, он перестает быть жадным и соответствует минимально допустимому количеству раз. Таким образом, шаблон /\*.*?\*/ корректно находит все комментарии языка Си. Использование символа '?' после квантификатора влияет исключительно на его жадность, и не затрагивает никакие другие свойства. Не следует путать использование символа '?' как, собственно, квантификатора (ноль либо одно соответствие) и как ограничителя жадности. Также в следствие его двойственной функциональности может использоваться следующая запись: \d??\d, которая в первую очередь соответствует одной цифре, но также может соответствовать и двум цифрам, если это необходимо для соответствия остальных частей шаблона.

В случае, если установлена опция PCRE_UNGREEDY (отсутствующая в Perl), квантификаторы являются не жадными по умолчанию, но могут становиться жадными, если за ними следует символ '?'. Говоря другими словами, знак вопроса инвертирует жадность квантификаторов.

В случае, если используется подмаска с квантификатором, для которого задано минимальное количество повторений (больше одного), либо если задано максимальное количество повторений, для откомпилированного шаблона требуется больше памяти (пропорционально миниму либо максимуму соответственно).

В случае, если шаблон начинается с .* либо .<0,>, и установлен модификатор PCRE_DOTALL (являющийся аналогом Perl-опции /s), который позволяет метасимволу "точка" соответствовать переводу строки, шаблон неявно заякоривается. Это происходит поскольку все последующие конструкции будут сопоставляться с каждой символьной позицией в обрабатываемом тексте, и, как следствие, начало строки - единственная позиция, дающая наиболее полное совпадение. PCRE рассматривает каждый такой шаблон, как если бы ему предшествовала последовательность \A. В случае, если известно, что данные не содержат переводов строк, а сам шаблон начинается на .*, рекоммендуется использовать PCRE_DOTALL для оптимизации шаблона, либо использовать метасимвол '^' для указания явного заякоривания.

В случае, если захватывающая подмаска повторяется, результирующим значением подмаски будет подстрока, совпадающая с результатом последней итерации. Например, после того, как (tweedle[dume]<3>\s*)+ совпадет с "tweedledum tweedledee", результирующим значением подмаски будет "tweedledee". Однако, если присутствуют вложенные захватывающие подмаски, соответствующие значения могут быть установлены в предыдущих итерациях. Например, после того, как /(a|(b))+/ совпадет с "aba", значением второй захваченной подстроки будет "b".

Обратные ссылки

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

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

Обратная ссылка сопоставляется с частью строки, захваченной соответствующей подмаской, но не с самой подмаской. Таким образом шаблон (sens|respons)e and \1ibility соответствует "sense and sensibility", "response and responsibility", но не "sense and responsibility". В случае, если обратная ссылка обнаружена во время регистрозависимого поиска, то при сопоставлении обратной ссылки регистр также учитывается. Например, ((?i)rah)\s+\1 соответствует "rah rah" и "RAH RAH", но не "RAH rah", хотя сама подмаска сопоставляется без учета регистра.

На одну и ту же подмаску может быть несколько ссылок. Если подмаска не участвовала в сопоставлении, то сопоставление со ссылкой на нее всегда терпит неудачу. Например, шаблон (a|(bc))\2 терпит неудачу, если находит соответствие с "a" раньше, чем с "bc". Поскольку может быть до 99 обратных ссылок, все цифры, следующие за обратным слешем, рассматриваются как часть потенциальной обратной ссылки. Если за ссылкой должна следовать цифра, необходимо использовать ограничитель. В случае, если указан флаг PCRE_EXTENDED, ограничителем может быть любой пробельный символ. В противном случае можно использовать пустой комментарий.

Ссылка на подмаску, внутри которой она расположена, всегда терпит неудачу, если это первое сопоставление текущей подмаски. Например, шаблон (a\1) не соответствует ни одной строке. Но все же такие ссылки бывают полезны в повторяющихся подмасках. Например, шаблон (a|b\1)+ совпадает с любым количеством "a", "aba", "ababaa". При каждой итерации подмаски обратная ссылка соответствует той части строки, которая была захвачена при предыдущей итерации. Чтобы такая конструкция работала, шаблон должен быть построен так, чтобы при первой итерации сопоставление с обратной ссылкой не производилось. Этого можно достичь, используя альтернативы (как в предыдущем примере) либо квантификаторы с минимумом, равным нулю.

Утверждения

Утверждения - это проверки касательно символов, идущих до или после текущей позиции сопоставления, ничего при этом не поглощая (никакие символы исходного текста не ставятся в соответствие утверждениям). Наиболее простые варианты утверждений, такие как \b, \B, \A, \Z, \z, ^ и $ были рассмотрены ранее. Более сложные утверждения записываются как подмаски. Утверждения бывают двух видов: те, которые анализируют текст, предшествующий текущей позиции, и идущий после нее.

Сопоставление подмаски, содержащий утверждение, происходит обычным образом, за исключением того, что текущая позиция не изменяется. Утверждения касательно последующего текста начинаются с (?= для положительных утверждений и с (?! для отрицающих утверждений. Например, \w+(?=;) совпадает со словом, за которым следует символ ';', но при этом сама точка с запятой в совпадение не включается. А foo(?!bar) соответствует любому появлению "foo", после которого не идёт "bar". Заметим, что похожий шаблон (?!foo)bar не будет искать вхождение "bar", которому предшествует любая строка за исключением "foo". Но, тем не менее, он будет соответствовать любым вхождениям подстроки "bar", поскольку условие (?!foo) всегда TRUE, если следующие три символа - "bar". Для получения желаемого результата необходимо воспользоваться второй категорией утверждений.

Утверждения касательно предшествующего текста начинаются с (? . Например: (?>\d+)bar

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

Говоря другими словами, подмаски такого типа соответствуют той части подстроки, которой соответствовала бы одиночная изолированная маска, заякоренная на текущей позиции обрабатываемого текста.

Однократные подмаски являются незахватывающими. Простые примеры, подобные приведенному выше, можно охарактеризовать как безусловный захват максимального количества повторений. В то время как \d+ и \d+? корректируются так, чтобы остальные части шаблона так же совпали, (?>\d+) соответствует исключительно максимальной по длине последовательности цифр, даже если это приводит к неудаче при сопоставлении других частей шаблона.

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

Однократные подмаски могут использоваться совместно с утверждениями касательно предшествующего текста для описания эффективных сопоставлений в конце обрабатываемого текста. Рассмотрим простой шаблон abcd$ в применении к длинному тексту, который не соответствует указанной маске. Поскольку поиск происходит слева направо, вначале PCRE будет искать букву "a", и только потом анализировать следующие записи в шаблоне. В случае, если шаблон указан в виде ^.*abcd$. В таком случае вначале .* сопоставляется со всей строкой, после чего сопоставление терпит неудачу (так как нет последующего символа 'a'). После чего .* сопоставляется со всей строкой, кроме последнего символа, потом кроме двух последних символов, и так далее. В конечном итоге поиск символа 'a' происходит по всей строке. Однако, если шаблон записать в виде: ^(?>.*)(? )*[!?] соответствует неограниченному количеству подстрок, которые состоят не из цифр, либо из цифр заключенных в <>, за которыми следует ? либо !. В случае, если в обрабатываемом тексте содержатся соответствия, время работы регулярного выражения будет невелико. Но если его применить к строке aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa это займет длительное время. Это связанно с тем, что строка может быть разделена между двумя частями шаблона многими способами, и все они будут опробованы (в примере мы использовали [?!], поскольку в случае одиночного символа в конце шаблона и PCRE и Perl выполняют оптимизацию. Они запоминают последний одиночный символ и в случае его отсутствия выдают неудачу). Если изменить шаблон на ((?>\D+)| )*[!?], нецифровые последовательности не могут быть разорваны, и невозможность сопоставления обнаруживается гораздо быстрее.

Условные подмаски

В PCRE реализована возможность подчинять шаблон условию либо выбирать из двух условных подмасок в зависимости от успеха сопоставления предыдущей подмаски. Условные подмаски имеют две допустимые формы использования:

В случае успешного сопоставления условия condition, используется подмаска yes-pattern, в противном случае no-pattern (если он присутствует). Если указано более двух альтернатив, возникнет ошибка во время компиляции.

Условия бывают двух видов. В случае, если между скобками заключены цифры, условие будет выполняться в том случае, если подмаска с соответствующим номером была успешно сопоставлена. Рассмотрим следующий шаблон (он содержит незначащий пробел для удобства чтения, подразумевается использование модификатора PCRE_EXTENDED), разделив его для удобства на три смысловые части: ( \( )? [^()]+ (?(1) \) )

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

В случае, если условие не является последовательностью цифр, оно обязано быть условием. Это также может быть утверждающее или отрицающее условие касательно предшествующего либо последующего текста. Рассмотрим еще один шаблон, также содержащий незначащий пробел и два условия, записанные в следующей строке:

Приведен пример с утверждающим условием касательно предшествующего текста, которое выполняется для необязательной последовательности не-букв с последующей буквой. Говоря другими словами, указанное условие проверяет наличие хотя бы одной предшествующей буквы. В случае, если буква найдена, выполняется сопоставление с первой альтернативой, в противном случае - со второй альтернативой. Приведенный шаблон соответствует строкам двух видов: dd-aaa-dd либо dd-dd-dd, где aaaa - это буквы, а dd - цифры.

Комментарии

Служебная последовательность (?# обозначает начало комментария, который продолжается до ближайшей закрывающей скобки. Вложенные скобки не допускаются. Символы, находящиеся внутри комментария, не принимают участия в сопоставлении шаблона.

В случае, если используется модификатор PCRE_EXTENDED, неэкранированный символ '#' вне символьного класса также означает начало блока комментария, который длится до конца текущей строки.

Рекурсивные шаблоны

Рассмотрим задачу поиска соответствия со строкой, находящихся внутри неограниченного количества круглых скобок. Без использования рекурсии лучшее, что можно сделать - написать шаблон, который будет решать задачу для некоторой ограниченной глубины вложенности, так как обработать неограниченную глубину не предоставляется возможным. В Perl 5.6 предоставлены некоторые экспериментальные возможности, которые в том числе позвояляют реализовать рекурсию в шаблонах. Специально обозначение (?R) используется для указания рекурсивной подмаски. Таким образом, приведем PCRE шаблон, решающий поставленную задачу (подразумевается, что используется модификатор PCRE_EXTENDED, незначащие пробелы игнорируются): \( ( (?>[^()]+) | (?R) )* \)

Вначале он соответствует открывающей круглой скобке. Далее он соответствует любому количеству подстрок, каждая из которых может быть последовательностью не-скобок, либо строкой, рекурсивно соответствующей шаблону (т.е. строкой, корректно заключенной в круглые скобки). И, в конце, идет закрывающая круглая скобка.

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

Значение, устанавливаемое для захватывающей подмаски будет соответствовать значению, захваченному на наиболее глубоком уровне рекурсии. В случае, если приведенный выше шаблон сопоставляется со строкой (ab(cd)ef), захваченным значением будет 'ef', которое является последним значением, принимаемым на верхнем уровне. В случае, если добавить дополнительные скобки \( ( ( (?>[^()]+) | (?R) )* ) \), захваченным значением будет "ab(cd)ef". В случае, если в шаблоне встречается более, чем 15 захватывающих скобок, PCRE требуется больше памяти для обработки рекурсии, чем обычно. Память выделяется при помощи функции pcre_malloc, и освобождается соответственно функцией pcre_free. Если память не может быть выделена, сохраняются данные только для первых 15 захватывающих скобок, так как нет способа выдать ошибку out-of-memory изнутри рекурсии.

Производительность

Некотрые элементы, которые могут встречаться в шаблонах, являются более эффективными, чем ряд других. Например, гораздо эффективней использовать символьный класс [aeiou] вместо набора альтернатив (a|e|i|o|u). Как правило, более простая конструкция является более эффективной. Книга Jeffrey Friedl'а содержит много обсуждений вопроса оптимизации регулярных выражений.

В случае, если шаблон начинается с .* и используется флаг PCRE_DOTALL, шаблон неявно заякоривается, так как он может совпадать только в начале строки. Но если PCRE_DOTALL не используется, PCRE не может выполнить соответствующую оптимизацию, так как в таком случае метасимвол '.' не соответствует символу начала строки (если обрабатываемые данные содержат переводы строк, такой шаблон может соответствовать шаблону не от начала строки, а от позиции непосредственно после перевода строки). Например, применяя шаблон (.*) second к строке "first\nand second" (где \n обозначает символ перевода строки), значение, захваченное первой подмаской, будет 'and'. Чтобы обработать все возможные точки соответствия, PCRE пытается сопоставить шаблон после каждого символа перевода строки.

В случае, если вы используете подобные шаблоны для обработки данных, не содержащих переводы строк, для лучшей производительности используйте модификатор PCRE_DOTALL, либо начинайте шаблон с ^.* для указания явного заякоривания. Это предотвратит PСRE от поиска символов новых строк и дополнительных попыток сопоставить шаблон с каждой такой найденной позицией.

Избегайте шаблонов, которые содержат вложенные неограниченные повторения. Сопоставление их со строками, не содержащими совпадений, занимает длительное время. Рассмотрим пример шаблона (a+)*

Он может соответствовать с "aaaa" 33-мя различными способами, и эта цифра очень быстро растет при увеличении строки. (В данном примере, квантификатор * может совпадать 0, 1, 2, 3 или 4 раза, и для каждого такого случая, кроме нуля, квантификатор + также может совпадать различное число раз.) Если остаток шаблона таков, что все совпадение терпит неуачу, PCRE должно попробовать все возможные варианты совпадения, что может потребовать огромного количества времени.

При помощи оптимизации можно отловить наиболее простые случаи, такие как (a+)*b где следом идёт литеральный символ. Прежде, чем производить стандартную процедуру поиска, PCRE проверяет в последующей подстроке наличие символа 'b', и, в случае отсутствия такового, попытка сопоставления немедленно завершается неудачей. Однако, когда последующего литерала нет, оптимизация не может быть применена. Вы можете ощутить разницу, сравнив поведение (a+)*\d с поведением приведенного выше шаблона. Первый определяет невозможность сопоставления практически сразу же, при сопоставлении со строкой состоящей из символов 'a', в то время как второй тратит длительное время на поиск в строках длинее 20 символов.

Часть 2. Регулярные выражения в конкретных программах

Серия контента:

Этот контент является частью # из серии # статей: Секреты регулярных выражений (regular expressions)

Этот контент является частью серии: Секреты регулярных выражений (regular expressions)

Следите за выходом новых статей этой серии.

1. Введение. Знание особенностей повышает эффективность

В предыдущей статье я приводил в основном примеры регулярных выражений без привязки к конкретной программе или языковой среде. Но каждая реализация механизма регулярных выражений имеет свои особенности, свои преимущества, которыми можно воспользоваться, свои недостатки, о которых следует знать, чтобы обходить их. Ведь регулярные выражения не существуют сами по себе, их применение неразрывно связано либо с некоторой утилитой (grep, sed, awk), либо с одним из языков программирования (Perl, Python, Tcl и т.д.).

Зная тонкости реализации механизма регулярных выражений в той или иной программной среде, можно существенно повысить эффективность их практического использования. Здесь я не рассматриваю регулярные выражения для утилит поиска grep/egrep/fgrep, потому что большинство примеров из первой статьи было ориентировано именно на эти программы.

2. Примеры использования регулярных выражений в Perl

Perl является "неофициальным чемпионом" по частоте использования в нём регулярных выражений для решения различных задач среди всех интерпретируемых или скриптовых языков. Несмотря на постоянно растущее скептическое отношение к Perl, он вполне справляется с той работой, для которой главным образом и предназначен – для обработки текстовых данных (вспомним один из вариантов "расшифровки" имени Perl – Practical Extraction and Report Language).

2.1. Корректная версия шаблона для поиска IP-адреса

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

будет находить и выводить строки вида "900.800.700.600", никоим образом к IP-адресам не относящиеся. Диалект простых регулярных выражений в данном случае не позволяет без непомерных затрат времени и сил решить эту проблему. Но в Perl реализованы расширенные регулярные выражения, что позволяет упростить решение.

В первой части IP-адреса может находиться трёхзначное число, начинающееся либо с "1" (за которой могут следовать две любые цифры), либо с "2" (но в этом случае число не должно быть больше 255), или любое двузначное число, или однозначное число (цифры от 1 до 9). На диалекте расширенных регулярных выражений для Perl это можно записать следующим образом:

Обратите внимание на использование новой конструкции группирования символов, которую часто называют дизъюнкцией: a|b|c – т.е. должен совпасть только один из указанных вариантов, – либо a, либо b, либо c. В нашем примере таких взаимоисключающих вариантов пять:

  • [1-9] – соответствует значениям от 1 до 9;
  • [1-9][0-9] – соответствует значениям от 10 до 99;
  • 1[0-9][0-9] – соответствует значениям от 100 до 199;
  • 2[0-4][0-9] – соответствует значениям от 200 до 249;
  • 25[0-5] – соответствует значениям от 250 до 255.

Одиночный нуль здесь исключается, так как обычные IP-адреса не содержат значение 0 в первом байте. Это выражение можно немного улучшить, если заменить диапазон [0-9] применяемым в Perl метасимволом \d (обозначение цифрового символа). После замены выражение приобретёт вид:

Немного короче, но ведь это шаблон только для самой первой части IP-адреса. Во второй и третьей частях допускаются нулевые значения (например, 10.0.0.1), поэтому для них шаблон нужно чуть-чуть изменить:

Шаблон четвёртого байта зависит от контекста поиска. Если вам нужны только IP-адреса хостов, то совпадение с одиночным символом "0" должно быть исключено, и шаблон будет таким же, как для самого первого байта. Если требуются ещё и адреса сетей (подсетей), то можно воспользоваться шаблоном для второго и третьего байтов адреса.

Осталось придать нашему шаблону поиска завершённый вид, который в Perl-скрипте может быть, например, таким:

Замечание. В Perl и шаблон регулярного выражения, и варианты в конструкции дизъюнкции записываются между парными символами "слэш" (/). Из-за этого слэши, ограничивающие варианты дизъюнкции, требуют предваряющих экранирующих символов "обратный слэш" (\). Конечно, подобная запись шаблона выглядит жутковато, но зато работает правильно.

2.2. Работа с данными, разделёнными запятыми

Многие системы управления базами данных и электронные таблицы поддерживают вывод в виде списков полей, разделённых запятыми, в качестве стандартного формата обмена данными. Этот формат обозначается аббревиатурой CSV (Comma-Separated Values – значения, разделённые запятой). На первый взгляд, решение задачи распределения таких данных по переменным с помощью Perl выглядит достаточно простым: использовать функцию split /,/ из набора штатных средств. Но внутри полей данных могут содержаться собственные запятые (в символьных строках или в числовых значениях денежных сумм в российских рублях). Что получится в результате обработки функцией split /,/ такой, например, строки данных: "Иванов", "инженер, расчётчик-математик", "4356,50 руб." ?

Чтобы обойти все эти "подводные камни", можно написать специализированную процедуру:

В приведённой выше процедуре первая часть шаблона позволяет выделить фрагмент исходной строки, заключённый в кавычки и ограниченный первой запятой, найденной вне этой пары кавычек. Внутри кавычек могут встречаться любые символы, в том числе и запятые. Вторая часть шаблона соответствует фрагменту без кавычек до первой следующей за ним запятой. Такой фрагмент тоже сохраняется в массиве fields. Последняя часть шаблона – запятая – завершает очередную итерацию цикла. Ключ g после шаблона означает его глобальное действие, т.е. запись в массив всех найденных фрагментов, а не только первого совпадающего. Ключ x позволяет игнорировать все "пробельные символы" в шаблоне (имеются в виду литеральные пробелы, а не метасимволы \s и escape-последовательности). Это немного облегчает чтение шаблона – можно вставить пробелы между символами дизъюнкции (вертикальная черта – разделитель вариантов).

2.3. Небольшие примеры использования Perl для поиска в тексте из командной строки

Нередко встречаются задачи поиска образцов, в условиях которых определено, что надо найти "образец1 И образец2 И образец3". Средства из группы grep легко справляются с задачами поиска одного из вариантов шаблона (образец1|образец2|образец3), но для предложенной задачи потребуется конвейер или другие ухищрения. С помощью Perl подобные задачи решаются "в одно действие":

Здесь ключ e позволяет определить строку выполняемых команд, а ключ n заставляет интерпретатор Perl считать, что заданная последовательность команд заключена в цикл while(<>), т.е. будет выполняться для всех строк перечисленных файлов.

В тех случаях, когда нужно найти абзацы, в которых встречаются все три указанных слова, поможет режим работы с абзацами. Для Perl этот режим активизируется ключом -00:

А если необходимо вывести список файлов, которые содержат все три слова, то для ключа -0 надо установить такой разделитель записей, который не содержится в обычных текстовых файлах, например, NUL-символ:

В общем, не спешите "хоронить" Perl – он ещё способен на многое, особенно там, где требуется интенсивная работа с регулярными выражениями.

3. Примеры использования регулярных выражений в Python

Диалект регулярных выражений языка Python довольно-таки близок к диалекту текстового редактора Emacs. Тем не менее в Python синтаксис записи регулярных выражений можно динамически корректировать в любой момент времени. Если вы устали от огромного количества обратных слэшей (те, кто пользовался регулярными выражениями в Emacs, сразу поймёт, что я имею в виду), то можете от них избавиться:

Первый устанавливаемый флаг говорит о том, что для группирования в шаблонах должны использоваться неэкранированные скобки, а второй флаг – о том, что в конструкции дизъюнкции (выбора варианта) – неэкранированная вертикальная черта. Скорректированные таким образом конструкции будут читаться гораздо легче.

В Python механизм регулярных выражений подключается с помощью модуля re. Поскольку Python по своей сущности является объектно-ориентированным языком, то это в полной мере относится и к его диалекту регулярных выражений. При необходимости создаётся объект типа "регулярное выражение", который в дальнейшем вы можете применять к строкам для выполнения поиска или замены. Рассмотрим следующий фрагмент кода:

В первой строке фрагмента создаётся объект-шаблон, соответствующий любой последовательности символов в тексте, начинающейся и заканчивающейся символами подчёркивания (например: "здесь _важно_ отметить"). После создания этот объект можно применять к любым строкам, используя его методы поиска и замены. В данном случае применяется метод замены sub(), который принимает в качестве аргументов строку замены и обрабатываемый текст input_text. Обратите внимание на элемент \1, обозначающий найденный фрагмент и соответствующий той части шаблона, которая заключена в круглые скобки. В отличие от Perl, обозначение \1 включается и в строку замены. В результате обработки текст (сохраняемый в result_text) будет заключён в HTML-тэги "подчёркнутый текст", например: "здесь важно отметить".

А вот как решается проблема с повторяющимися словами-опечатками ("не не", "для для" и т.д.) на языке Python:

В этой программе все обнаруженные повторения-опечатки выводятся на стандартное устройство с указанием имени файла, в котором они были найдены, и выделяются подсветкой при помощи esc-последовательностей (символ ESC \033).

4. Примеры использования регулярных выражений в Tcl

В Tcl для работы с регулярными выражениями имеются две функции: regexp – для поиска и regsub – для поиска с заменой и созданием копии исходной строки. Синтаксис вызова функции regexp:

Если совпадение с регулярным выражением найдено в строке поиска, то функция возвращает 1, в противном случае – 0. В строку-приёмник (если она задана) копируется совпавший фрагмент. Если заданы имена нескольких строк-приёмников, то им последовательно присваиваются фрагменты, совпавшие с элементами шаблона в круглых скобках, а тем, кому "не хватило" совпадений, присваиваются пустые строки. Если не обнаружено ни одного совпадения с регулярным выражением, то строки-приёмники не изменяются.

Предположим, что имеется файл с записями автомобильных номерных знаков и фамилиями владельцев соответствующих автомобилей. Поиск на языке Tcl может быть записан следующим образом:

В этом примере будут найдены все владельцы автомобилей с "числом зверя" в номере вне зависимости от регистра букв, которыми записан номерной знак (ключ -nocase). Весь совпавший фрагмент не будет сохранён, так как на первом месте в списке строк-приёмников стоит пара фигурных скобок <>, а не имя переменной. Первый фрагмент в скобках (номерной знак) запоминается в переменной num, второй фрагмент в скобках (фамилия владельца) – в переменной owner. Затем значения этих переменных выводятся.

Функция regsub работает аналогично функции regexp:

Отличие состоит лишь в том, что после строки поиска записывается строка замены, а строка-приёмник может быть задана только одна.

5. Примеры использования регулярных выражений в sed

Потоковый редактор sed используется главным образом для выполнения массовых замен в текстовых файлах, обрабатываемых построчно. Всем, кто хотя бы немного знаком с sed, известна классическая команда удаления всех пустых строк в обрабатываемом файле:

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

Здесь под обозначением TAB подразумевается "настоящий", литеральный символ табуляции (генерируемый при нажатии клавиши Tab на клавиатуре).

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

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

Если же, напротив, в начале каждой не пустой строки требуется вставка, например, пары пробелов, то можно воспользоваться функцией логического отрицания:

6. Заключение

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

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


Статьи

Строки и регулярные выражения в PHP

Введение

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

Поиск по шаблону позволяет не только находить определенные фрагменты текста, но и заменять их другими фрагментами. Одним из стандартных примеров поиска по шаблону являются команды поиска/замены в текстовых редакторах - например, в MS Word, Emacs и в моем любимом редакторе vi. Всем пользователям UNIX хорошо известны такие программы, как sed, awk и grep; богатство возможностей этих программ в значительной степени обусловлено средствами поиска по шаблону. Механизмы поиска по шаблону решают четыре основные задачи:

  • поиск строк, в точности совпадающих с заданным шаблоном;
  • поиск фрагментов строк, совпадающих с заданным шаблоном;
  • замену строк и подстрок по шаблону;
  • поиск строк, с которыми заданный шаблон не совпадает.

Появление Web породило необходимость в более быстрых и эффективных средствах поиска данных, которые бы позволяли пользователям со всего мира находить нужную информацию среди миллиардов web-страниц. Поисковые системы, онлайновые финансовые службы и сайты электронной коммерции — все это стало бы абсолютно бесполезным без средств анализа гигантских объемов данных в этих секторах.

Действительно, средства обработки строковой информации являются жизненно важной составляющей практически любого сектора, так или иначе связанного с современными информационными технологиями. В этой главе основное внимание посвящено средствам обработки строк в РНР. Мы рассмотрим некоторые стандартные строковые функции (в языке их больше 60!), а из приведенных определений и примеров вы получите сведения, необходимые для создания web-приложений. Но прежде чем переходить к специфике РНР, я хочу познакомить вас с базовым механизмом, благодаря которому становится возможным поиск по шаблону. Речь идет о регулярных выражениях.

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

Регулярные выражения лежат в основе всех современных технологий поиска по шаблону. Регулярное выражение представляет собой последовательность простых и служебных символов, описывающих искомый текст. Иногда регулярные выражения бывают простыми и понятными (например, слово dog ), но часто в них присутствуют служебные символы, обладающие особым смыслом в синтаксисе регулярных выражений, — например, .* .

В РНР существуют два семейства функций, каждое из которых относится к определенному типу регулярных выражений: в стиле POSIX или в стиле Perl. Каждый тип регулярных выражений обладает собственным синтаксисом и рассматривается в соответствующей части главы. На эту тему были написаны многочисленные учебники, которые можно найти как в Web, так и в книжных магазинах. Поэтому я приведу лишь основные сведения о каждом типе, а дальнейшую информацию при желании вы сможете найти самостоятельно. Если вы еще не знакомы с принципами работы регулярных выражений, обязательно прочитайте краткий вводный курс, занимающий всю оставшуюся часть этого раздела. А если вы хорошо разбираетесь в этой области, смело переходите к следующему разделу.

Синтаксис регулярных выражений (POSIX)

Структура регулярных выражений POSIX чем-то напоминает структуру типичных математических выражений - различные элементы (операторы) объединяются друг с другом и образуют более сложные выражения. Однако именно смысл объединения элементов делает регулярные выражения таким мощным и выразительным средством. Возможности не ограничиваются поиском литерального текста (например, конкретного слова или числа); вы можете провести поиск строк с разной семантикой, но похожим синтаксисом — например, всех тегов HTML в файле.

Простейшее регулярное выражение совпадает с одним литеральным символом — например, выражение g совпадает в таких строках, как g , haggle и bag . Выражение, полученное при объединении нескольких литеральных символов, совпадает по тем же правилам — например, последовательность gan совпадает в любой строке, содержащей эти символы (например, gang , organize или Reagan ).

Оператор | (вертикальная черта) проверяет совпадение одной из нескольких альтернатив. Например, регулярное выражение php | zend проверяет строку на наличие php или zend .

Квадратные скобки

Квадратные скобки ( [ ] ) имеют особый смысл в контексте регулярных выражений — они означают "любой символ из перечисленных в скобках". В отличие от регулярного выражения php , которое совпадает во всех строках, содержащих литеральный текст php , выражение [php] совпадает в любой строке, содержащей символы р или h . Квадратные скобки играют важную роль при работе с регулярными выражениями, поскольку в процессе поиска часто возникает задача поиска символов из заданного интервала. Ниже перечислены некоторые часто используемые интервалы:

  • [0-9] — совпадает с любой десятичной цифрой от 0 до 9 ;
  • [a-z] — совпадает с любым символом нижнего регистра от а до z ;
  • [A-Z] — совпадает с любым символом верхнего регистра от А до Z ;
  • [a-Z] — совпадает с любым символом нижнего или верхнего регистра от а до Z .

Конечно, перечисленные выше интервалы всего лишь демонстрируют общий принцип. Например, вы можете воспользоваться интервалом [0-3] для обозначения любой десятичной цифры от 0 до 3 или интервалом [b-v] для обозначения любого символа нижнего регистра от b до v . Короче говоря, интервалы определяются совершенно произвольно.

Квантификаторы

Существует особый класс служебных символов, обозначающих количество повторений отдельного символа или конструкции, заключенной в квадратные скобки. Эти служебные символы ( + , * и <. >) называются квантификаторами. Принцип их действия проще всего пояснить на примерах:

  • р+ означает один или несколько символов р , стоящих подряд;
  • р* означает ноль и более символов р , стоящих подряд;
  • р? означает ноль или один символ р ;
  • р <2>означает два символа р , стоящих подряд;
  • р <2,3>означает от двух до трех символов р , стоящих подряд;
  • р <2,>означает два и более символов р , стоящих подряд.

Прочие служебные символы

Служебные символы $ и ^ совпадают не с символами, а с определенными позициями в строке. Например, выражение р$ означает строку, которая завершается символом р , а выражение ^р - строку, начинающуюся с символа р .

  • Конструкция [^a-zA-Z] совпадает с любым символом, не входящим в указаные интервалы ( a-z и A-Z ).
  • Служебный символ . (точка) означает "любой символ". Например, выражение р.р совпадает с символом р , за которым следует произвольный символ, после чего опять следует символ р .

Объединение служебных символов приводит к появлению более сложных выражений. Рассмотрим несколько примеров:

  • ^.<2>$ — любая строка, содержащая ровно два символа;
  • (.*) — произвольная последовательность символов, заключенная между и (вероятно, тегами HTML для вывода жирного текста);
  • p(hp)* — символ р , за которым следует ноль и более экземпляров последовательности hp (например, phphphp ).

Иногда требуется найти служебные символы в строках вместо того, чтобы использовать их в описанном специальном контексте. Для этого служебные символы экранируются обратной косой чертой ( \ ). Например, для поиска денежной суммы в долларах можно воспользоваться выражением \$[0-9]+ , то есть "знак доллара, за которым следует одна или несколько десятичных цифр". Обратите внимание на обратную косую черту перед $ . Возможными совпадениями для этого регулярного выражения являются $42 , $560 и $3 .

Стандартные интервальные выражения (символьные классы)

Для удобства программирования в стандарте POSIX были определены некоторые стандартные интервальные выражения, также называемые символьными классами ( character classes ). Символьный класс определяет один символ из заданного интервала - например, букву алфавита или цифру:

  • [[:alpha:]] — алфавитный символ (a-zA-Z);
  • [[:digit:]] — цифра (0-9);
  • [[:alnum:]] — алфавитный символ (aA-zZ) или цифра (0-9);
  • [[:space:]] — пропуски (символы новой строки, табуляции и т. д.).

Функции РНР для работы с регулярными выражениями (POSIX-совместимые)

В настоящее время РНР поддерживает семь функций поиска с использованием регулярных выражений в стиле POSIX:

Описания этих функций приведены в следующих разделах.

Функция еrеg( ) ищет в заданной строке совпадение для шаблона. Если совпадение найдено, возвращается TRUE , в противном случае возвращается FALSE . Синтаксис функции ereg( ) :

Поиск производится с учетом регистра алфавитных символов. Пример использования ereg( ) для поиска в строках доменов .соm :

Функция возвращает TRUE , если $email завершается символами ".com" . В частности, поиск будет успешным для строк "www.wjgilmore.com" и "someemail@apress.com" .

Обратите внимание: из-за присутствия служебного символа $ регулярное выражение совпадает только в том случае, если строка завершается символами .com . Например, оно совпадет в строке "www.apress.com" , но не совпадет в строке "www.apress.com/catalog" .

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

При выполнении сценария будет получен следующий результат:

ereg_replace( )

Функция ereg_replace( ) ищет в заданной строке совпадение для шаблона и заменяет его новым фрагментом. Синтаксис функции ereg_replace( ) :

Функция ereg_replace( ) работает по тому же принципу, что и ereg( ) , но ее возможности расширены от простого поиска до поиска с заменой. После выполнения замены функция возвращает модифицированную строку. Если совпадения отсутствуют, строка остается в прежнем состоянии. Функция ereg_replace( ) , как и еrеg( ) , учитывает регистр символов. Ниже приведен простой пример, демонстрирующий применение этой функции:

У средств поиска с заменой в языке РНР имеется одна интересная возможность - возможность использования обратных ссылок на части основного выражения, заключенные в круглые скобки. Обратные ссылки похожи на элементы необязательного параметра-массива совпадения функции еrеg( ) за одним исключением: обратные ссылки записываются в виде \0 , \1 , \2 и т. д., где \0 соответствует всей строке, \1 - успешному совпадению первого подвыражения и т. д. Выражение может содержать до 9 обратных ссылок. В следующем примере все ссылки на URL в тексте заменяются работающими гиперссылками:

eregi( )

Функция eregi( ) ищет в заданной строке совпадение для шаблона. Синтаксис функции eregi( ):

Поиск производится без учета регистра алфавитных символов. Функция eregi( ) особенно удобна при проверке правильности введенных строк (например, паролей). Использование функции eregi( ) продемонстрировано в следующем примере:

eregi_replace( )

Функция eregi_replасе( ) работает точно так же, как ereg_replace( ) , за одним исключением: поиск производится без учета регистра символов. Синтаксис функции ereg_replace( ) :

split( )

Функция split( ) разбивает строку на элементы, границы которых определяются по заданному шаблону. Синтаксис функции split( ) :

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

spliti( )

Функция spliti( ) работает точно так же, как ее прототип split( ) , за одним исключением: она не учитывает регистра символов. Синтаксис функции spliti( ) :

Разумеется, регистр символов важен лишь в том случае, если шаблон содержит алфавитные символы. Для других символов выполнение spliti( ) полностью аналогично split( ) .

sql_regcase( )

Вспомогательная функция sql_regcase( ) заключает каждый символ входной строки в квадратные скобки и добавляет к нему парный символ. Синтаксис функции sql_regcase( ) :

Если алфавитный символ существует в двух вариантах (верхний и нижний регистры), выражение в квадратных скобках будет содержать оба варианта; в противном случае исходный символ повторяется дважды. Функция sql_regcase( ) особенно удобна при использовании РНР с программными пакетами, поддерживающими регулярные выражения в одном регистре. Пример преобразования строки функцией sql_regcase( ) :

Синтаксис регулярных выражений в стиле Perl

Perl давно считается одним из самых лучших языков обработки текстов. Синтаксис Perl позволяет осуществлять поиск и замену даже для самых сложных шаблонов. Разработчики РHР сочли, что не стоит заново изобретать уже изобретенное, а лучше сделать знаменитый синтаксис регулярных выражений Perl доступным для пользователей РНР. Так появились функции для работы с регулярными выражениями в стиле Perl.

Диалект регулярных выражений Perl не так уж сильно отличается от диалекта POSIX. В сущности, синтаксис регулярных выражений Perl является отдаленным потомком реализации POSIX, вследствие чего синтаксис POSIX почти совместим с функциями регулярных выражений стиля Perl.

Оставшаяся часть этого раздела будет посвящена краткому знакомству с диалектом регулярных выражений Perl. Рассмотрим простой пример:

Обратите внимание: строка food заключена между двумя косыми чертами. Как и в стандарте POSIX, вы можете создавать более сложные шаблоны при помощи квантификаторов:

Этот шаблон совпадает с последовательностью fo , за которой могут следовать дополнительные символы о . Например, совпадения будут обнаружены в строках food , fool и fo4 . Рассмотрим другой пример использования квантификатора:

Шаблон совпадает с символом f , за которым следуют от 2 до 4 экземпляров символа о . К числу потенциальных совпадений относятся строки fool , fooool и foosball .

В регулярных выражениях Perl могут использоваться все квантификаторы, упомянутые в предыдущем разделе для регулярных выражений POSIX.

Метасимволы

Одной из интересных особенностей Perl является использование метасимволов при поиске. Метасимвол [Следует отметить, что авторское толкование термина "метасимвол" противоречит не только всем традициям, по и официальной документации РНР. — Примеч. перев.] представляет собой алфавитный символ с префиксом \ - признаком особой интерпретации следующего символа. Например, метасимвол \d может использоваться при поиске денежных сумм:

Комбинация \d обозначает любую цифру. Конечно, в процессе поиска часто возникает задача идентификации алфавитно-цифровых символов, поэтому в Perl для них был определен метасимвол \w :

Этот шаблон совпадает с конструкциями, заключенными в угловые скобки, - например, тёгами HTML. Кстати, метасимвол \W имеет прямо противоположный смысл и используется для идентификации символов, не являющихся алфавитно-цифровыми.

Еще один полезный метасимвол, \b , совпадает с границами слов:

Поскольку метасимвол границы слова расположен справа от текста, этот шаблон совпадет в строках salsa и lisa , но не в строке sand . Противоположный метасимвол, \В , совпадает с чем угодно, кроме границы слова:

Шаблон совпадает в таких строках, как sand и Sally, но не совпадает в строке salsa.

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

Модификаторы заметно упрощают работу с регулярными выражениями. Впрочем, модификаторов много, и в таблице приведены лишь наиболее интересные из них. Модификаторы перечисляются сразу же после регулярного выражения - например, /string/i .

Модификатор Описание
m Фрагмент текста интерпретируется как состоящий из нескольких "логических строк". По умолчанию специальные символы ^ и $ совпадают только в начале и в конце всего фрагмента. При включении "многострочного режима" при помощи модификатора m ^ и $ будут совпадать в начале и в конце каждой логической строки внутри фрагмента
s По смыслу противоположен модификатору m - при поиске фрагмент интерпретируется как одна строка, а все внутренние символы новой строки игнорируются
i Поиск выполняется без учета регистра символов

Функции РНР для работы с регулярными выражениями (Perl-совместимые)

В РНР существует пять функций поиска по шаблону с использованием Perl-совместимых регулярных выражений:

  • preg_match( )
  • preg_match_all( )
  • preg_replace( )
  • preg_split( )
  • preg_grep( )

preg_match( )

Функция preg_match( ) ищет в заданной строке совпадение для шаблона. Если совпадение найдено, возвращается TRUE , в противном случае возвращается FALSE . Синтаксис функции preg_match( ) :

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

preg_match_all( )

Функция preg_match_all( ) находит все совпадения шаблона в заданной строке. Синтаксис функции preg_match_all( ) :

Порядок сохранения в массиве совпадения текста, совпавшего с подвыражениями, определяется необязательным параметром порядок . Этот параметр может принимать два значения:

  • PREG_PATTERN_ORDER - используется по умолчанию, если параметр порядок не указан. Порядок, определяемый значением PREG_PATTERN_ORDER, на первый взгляд выглядит не совсем логично: первый элемент (с индексом 0 ) содержит массив совпадений для всего регулярного выражения, второй элемент (с индексом 1 ) содержит массив всех совпадений для первого подвыражения в круглых скобках и т. д.;
  • PREG_SET_ORDER - порядок сортировки массива несколько отличается от принятого по умолчанию. Первый элемент (с индексом 0 ) содержит массив с текстом, совпавшим со всеми подвыражениями в круглых скобках для первого найденного совпадения. Второй элемент (с индексом 1 ) содержит аналогичный массив для второго найденного совпадения и т. д.

Следующий пример показывает, как при помощи функции preg_match_al( ) найти весь текст, заключенный между тегами HTML . :

preg_replace( )

Функция preg_replace( ) работает точно так же, как и ereg_replасе( ) , за одним исключением - регулярные выражения могут использоваться в обоих параметрах, шаблон и замена. Синтаксис функции preg_replace( ) :

Необязательный параметр порог определяет максимальное количество замен в строке. Интересный факт: параметры шаблон и замена могут представлять собой масивы. Функция preg_replace( ) перебирает элементы обоих массивов и выполняет замену по мере их нахождения.

preg_split( )

Функция preg_split( ) аналогична split( ) за одним исключением - параметр шаблон может содержать регулярное выражение. Синтаксис функции preg_split( ) :

Необязательный параметр порог определяет максимальное количество элементов, на которые делится строка. В следующем примере функция preg_split( ) используется для выборки информации из переменной.

preg_grep( )

Функция preg_grep( ) перебирает все элементы заданного массива и возвращает все элементы, в которых совпадает заданное регулярное выражение. Синтаксис функции preg_grep() :

Пример использования функции preg_grep( ) для поиска в массиве слов, начинающихся на р :

Другие строковые функции

Кроме функций для работы с регулярными выражениями, описанными в первой части этой главы, в РНР существует более 70 функций для выполнения практически всех мыслимых операций со строками. Подробное перечисление и описание всех функций выходит за рамки этой книги и приведет к обычному повторению информации, приведенной в документации РНР. По этой причине я превратил оставшуюся часть главы в своего рода список FAQ из вопросов, часто встречающихся во многих электронных конференциях РНР и на многих сайтах этой тематики. На мой взгляд, этот способ позволяет гораздо эффективнее описать общие принципы громадной библиотеки строковых функций РНР.

Дополнение и сжатие строк

В процессе форматирования часто возникает необходимость в изменении длины строки посредством дополнения или удаления символов. В РНР существует несколько функций, предназначенных для решения этой задачи.

Функция chop( ) возвращает строку после удаления из нее завершающих пропусков и символов новой строки. Синтаксис функции chop( ) :

В следующем примере функция chop( ) удаляет лишние символы новой строки:

Функция str_pad( ) выравнивает строку до определенной длины заданными символами и возвращает отформатированную строку. Синтаксис функции str_pad( ) :

Если необязательный параметр дополнение не указан, строка дополняется пробелами. В противном случае строка дополняется заданными символами. По умолчанию строка дополняется справа; тем не менее, вы можете передать в параметре тип_дополнения константу STR_PAD_RIGHT , STR_PAD_LEFT или STR_PAD_BOTH , что приведет к дополнению строки в заданном направлении. Пример демонстрирует дополнение строки функцией str_pad( ) с параметрами по умолчанию:

В следующем примере используются необязательные параметры функции str_pad( ) :

Функция trim( ) удаляет псе пропуски с обоих краев строки и возвращает полученную строку. Синтаксис функции trim( ) :

К числу удаляемых пропусков относятся и специальные символы \n, \r, \t, \v и \0.

Функция lrim( ) удаляет все пропуски и специальные символы с левого края строки и возвращает полученную строку. Синтаксис функции ltrim( ) :

Функция удаляет те же специальные символы, что и функция trim( ) .

Определение длины строки

Длину строки в символах можно определить при помощи функции strlen( ) . Синтаксис функции strlen( ) :

Следующий пример демонстрирует определение длины строки функцией strlen( ) :

Сравнение двух строк

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

Функция strcmp( ) сравнивает две строки с учетом регистра символов. Синтаксис функции strcmp( ) :

После завершения сравнения strcmp( ) возвращает одно из трех возможных значений:

  • 0 , если строка1 и строка2 совпадают;
  • , если строка1 меньше, чем строка2 ;
  • > 0 , если строка2 меньше, чем строка1 .

В следующем фрагменте сравниваются две одинаковые строки:

Функция strcasecmp( ) работает точно так же, как strcmp( ) , за одним исключением - регистр символов при сравнении не учитывается. Синтаксис функции strcasecmp( ) :

В следующем фрагменте сравниваются две одинаковые строки:

Функция strspn( ) возвращает длину первого сегмента строки1, содержащего символы, присутствующие в строке2. Синтаксис функции strspn( ):

Следующий фрагмент показывает, как функция strspn( ) используется для проверки пароля:

Функция strcspn( ) возвращает длину первого сегмента строки1, содержащего символы, отсутствующие в строке2. Синтаксис функции strcspn( ):

В следующем фрагменте функция strcspn( ) используется для проверки пароля:

Обработка строковых данных без применения регулярных выражений

При обработке больших объемов информации функции регулярных выражений сильно замедляют выполнение программы. Эти функции следует применять лишь при обработке относительно сложных строк, в которых регулярные выражения действительно необходимы. Если же анализ текста выполняется по относительно простым правилам, можно воспользоваться стандартными функциями РНР, которые заметно ускоряют обработку. Все эти функции описаны ниже.

Функция strtok( ) разбивает строку на лексемы по разделителям, заданным вторым параметром. Синтаксис функции strtok( ):

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

Функция parse_str( ) выделяет в строке пары и присваивает значения переменных в текущей области видимости. Синтаксис функции parse_str( ):

Функция parse_str( ) особенно удобна при обработке URL, содержащих данные форм HTML или другую расширенную информацию. В следующем примере анализируется информация, переданная через URL. Строка представляет собой стандартный способ передачи данных между страницами либо откомпилированных в гиперссылке, либо введенных в форму HTML:

Поскольку эта функция создавалась для работы с URL, она игнорирует символ амперсанд (&).

Функция explode( ) делит строку на элементы и возвращает эти элементы в виде массива. Синтаксис функции explode( ):

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

Функция explode( ) практически идентична функции регулярных выражений POSIX split( ), описанной выше. Главное различие заключается в том, что передача регулярных выражений в параметрах допускается только при вызове split( ).

Если функция explode( ) разделяет строку на элементы массива, то ее двойник - функция implode( ) - объединяет массив в строку. Синтаксис функции implode( ):

Формирование строки из массива продемонстрировано в следующем примере:

У implode( ) имеется псевдоним - функция join( ).

Функция strpos( ) находит в строке первый экземпляр заданной подстроки. Синтаксис функции strpos( ):

Необязательный параметр offset задает позицию, с которой должен начинаться поиск. Если подстрока не найдена, strpos( ) возвращает FALSE.

В следующем примере определяется позиция первого вхождения даты в файл журнала:

Функция strrpos( ) находит в строке последний экземпляр заданного символа. Синтаксис функции strrpos( ):

По возможностям эта функция уступает своему двойнику - функции strpos( ), поскольку она позволяет искать только отдельный символ, а не всю строку. Если во втором параметре strrpos( ) передается строка, при поиске будет использован только ее первый символ.

Функция str_replace( ) ищет в строке все вхождения заданной подстроки и заменяет их новой подстрокой. Синтаксис функции str_replace( ):

Функция substr_replace( ), описанная ниже в этом разделе, позволяет провести замену лишь в определенной части строки. Ниже показано, как функция str_replace( ) используется для проведения глобальной замены в строке.

Если подстрока ни разу не встречается в строке, исходная строка не изменяется:

Функция strstr( ) возвращает часть строки, начинающуюся с первого вхождения заданной подстроки. Синтаксис функции strstr( ):

В следующем примере функция strstr( ) используется для выделения имени домена из URL:

Функция substr( ) возвращает часть строки, начинающуюся с заданной начальной позиции и имеющую заданную длину. Синтаксис функции substr( ):

Если необязательный параметр длина не указан, считается, что подстрока начинается с заданной начальной позиции и продолжается до конца строки. При использовании этой функции необходимо учитывать четыре обстоятельства:

  • если параметр начало положителен, возвращаемая подстрока начинается с позиции строки с заданным номером;
  • если параметр начало отрицателен, возвращаемая подстрока начинается с позиции ( длина-начало );
  • если параметр длина положителен, в возвращаемую подстроку включаются все символы от позиции начало до позиции начало+длина . Если последняя величина превышает длину строки, возвращаются символы до конца строки;
  • если параметр длина отрицателен, возвращаемая подстрока заканчивается на заданном расстоянии от конца строки.

Помните о том, что параметр начало определяет смещение от первого символа строки; таким образом, возвращаемая строка в действительности начинается с символа с номером ( начало+1 ).

Следующий пример демонстрирует выделение части строки функцией substr( ):

Пример с положительным параметром длина :

Пример с отрицательным параметром длина :

Функция substr_count( ) возвращает количество вхождений подстроки в заданную строку. Синтаксис функции substr_count( ):

В следующем примере функция substr_count( ) подсчитывает количество вхождений подстроки ain:

Функция substr_replace( ) заменяет часть строки, которая начинается с заданной позиции. Если задан необязательный параметр длина, заменяется фрагмент заданной длины; в противном случае производится замена по всей длине заменяющей строки. Синтаксис функции substr_replace( ):

Параметры начало и длина задаются по определенным правилам:

  • если параметр начало положителен, замена начинается с заданной позиции;
  • если параметр начало отрицателен, замена начинается с позиции ( длина-начало );
  • если параметр длина положителен, заменяется фрагмент заданной длины;
  • если параметр длина отрицателен, замена завершается в позиции ( длина-длина ).

Простая замена текста функцией substr_replace( ) продемонстрирована в следующем примере:

Преобразование текста в HTML

Быстрое преобразование простого текста к формату web-браузера - весьма распространенная задача. В ее решении вам помогут функции, описанные в этом разделе.

Функция nl2br( ) заменяет все символы новой строки (\n) эквивалентными конструкциями HTML
. Синтаксис функции nl2br( ):

Символы новой строки могут быть как видимыми (то есть явно включенными в строку), так и невидимыми (например, введенными в редакторе). В следующем примере текстовая строка преобразуется в формат HTML посредством замены символов \n разрывами строк:

При последующем выводе $html_recipe браузеру будет передан следующий текст в формате HTML:

Функция htmlentities( ) преобразует символы в эквивалентные конструкции HTML. Синтаксис функции htmlentities:


В следующем примере производится необходимая замена символов строки для вывода в браузере:

Функция htmlentities( ) в настоящее время работает только для символов кодировки ISO-8559-1 (ISO-Latin-1). Кроме того, она не преобразует пробелы в , как следовало бы ожидать.

Функция htmlspecialchars( ) заменяет некоторые символы, имеющие особый смысл в контексте HTML, эквивалентными конструкциями HTML. Синтаксис функции htmlspecialchars( ):

Функция html special chars( ) в настоящее время преобразует следующие символы:

  • & преобразуется в &
  • " преобразуется в "
  • преобразуется в >.

В частности, эта функция позволяет предотвратить ввод пользователями разметки HTML в интерактивных web-приложениях (например, в электронных форумах). Ошибки, допущенные в разметке HTML, могут привести к тому, что вся страница будет формироваться неправильно. Впрочем, у этой задачи существует и более эффективное решение - полностью удалить теги из строки функцией strip_tags( ).

Следующий пример демонстрирует удаление потенциально опасных символов функцией htmlspeclalchars( ):

Если функция htmlspecialchars( ) используется в сочетании с nl2br( ), то последнюю следует вызывать после htmlspecialchars( ). В противном случае конструкции
, сгенерированные при вызове nl2br( ), преобразуются в видимые символы.

Функция get_html_translation_table( ) обеспечивает удобные средства преобразования текста в эквиваленты HTML Синтаксис функции get_htrril_translation_table( ):

Функция get_html_translation_table( ) возвращает одну из двух таблиц преобразования (определяется параметром таблица), используемых в работе стандартных функций htmlspecialchars( ) и htmlentities( ). Возвращаемое значение может использоваться в сочетании с другой стандартной функцией, strtr( ) (см. далее), для преобразования текста в код HTML.

Параметр таблица принимает одно из двух значений:

В следующем примере функция get_html_translation_table( ) используется при преобразовании текста в код HTML:

Кстати, функция array_flip( ) позволяет провести преобразование текста в HTML в обратном направлении и восстановить исходный текст. Предположим, что вместо вывода результата strtr( ) в предыдущем примере мы присвоили его переменной $translated string.

В следующем примере исходный текст восстанавливается функцией array_flip( ):

Функция strtr( ) транслирует строку, то есть заменяет в ней все символы, входящие в строку источник, соответствующими символами строки приемник. Синтаксис функции strtr( ):

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

Существует альтернативный синтаксис вызова strtr( ) с двумя параметрами; в этом случае второй параметр содержит ассоциативный массив, ключи которого соответствуют заменяемым подстрокам, а значения - заменяющим подстрокам. В следующем примере теги HTML заменяются XML-подобными конструкциями:

Преобразование HTML в простой текст

Иногда возникает необходимость преобразовать файл в формате HTML в простой текст. Функции, описанные ниже, помогут вам в решении этой задачи.

Функция strip_tags( ) удаляет из строки все теги HTML и РНР, оставляя в ней только текст. Синтаксис функции strip_tags( ):

Необязательный параметр разрешенные_теги позволяет указать теги, которые должны пропускаться в процессе удаления.

Ниже приведен пример удаления из строки всех тегов HTML функцией strip_tags( ):

В следующем примере удаляются не все, а лишь некоторые теги:

Хотя функция get_meta_tags( ) и не имеет прямого отношения к преобразованию текста, зто весьма полезная функция, о которой следует упомянуть. Синтаксис функции get_meta_tags( ):

Функция get_meta_tags( ) предназначена для поиска в файле HTML тегов МЕТА.

Теги МЕТА содержат информацию о странице, используемую главным образом поисковыми системами. Эти теги находятся внутри пары тегов . . Применение тегов МЕТА продемонстрировано в следующем фрагменте (назовем его example.html, поскольку он будет использоваться в листинге):

Функция get_meta_tags( ) ищет в заголовке документа теги, начинающиеся словом МЕТА, и сохраняет имена тегов и их содержимое в ассоциативном массиве. В листинге продемонстрировано применение этой функции к файлу example.html.

Извлечение тегов МЕТА из файла HTML функцией get_meta_tags( )

Интересная подробность: данные тегов МЕТА можно извлекать не только из файлов, находящихся на сервере, но и из других URL.

Преобразование строки к верхнему и нижнему регистру

В РНР существует четыре функции, предназначенных для изменения регистра строки:

Функция strtolower( ) преобразует все алфавитные символы строки к нижнему регистру. Синтаксис функции strtolower( ):

Неалфавитные символы функцией не изменяются. Преобразование строки к нижнему регистру функцией strtolower( ) продемонстрировано в следующем примере:

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

Неалфавитные символы функцией не изменяются. Преобразование строки к верхнему регистру функцией strtoupper( ) продемонстрировано в следующем примере:

Функция ucfirst( ) преобразует к верхнему регистру первый символ строки - при условии, что он является алфавитным символом. Синтаксис функции ucfirst( ):

Неалфавитные символы функцией не изменяются. Преобразование первого символа строки функцией ucfirst( ) продемонстрировано в следующем примере:

Функция ucwords( ) преобразует к верхнему регистру первую букву каждого слова в строке. Синтаксис функции ucwords( ):

Неалфавитные символы функцией не изменяются. определяется как последовательность символов, отделенная от других элементов строки пробелами. В следующем примере продемонстрировано преобразование первых символов слов функцией ucwords( ):

Проект: идентификация браузера

Каждый программист, пытающийся создать удобный web-сайт, должен учитывать различия в форматировании страниц при просмотре сайта в разных браузерах и операционных системах. Хотя консорциум W3 (http://www.w3.org) продолжает публиковать стандарты, которых должны придерживаться программисты при создании web-приложений, разработчики браузеров любят дополнять эти стандарты своими маленькими , что в конечном счете вызывает хаос и путаницу. Разработчики часто решают эту проблему, создавая разные страницы для каждого типа браузера и операционной системы - при этом объем работы значительно увеличивается, но зато итоговый сайт идеально подходит для любого пользователя. Результат - хорошая репутация сайта и уверенность в том, что пользователь посетит его снова.

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

Приведенный ниже проект (sniffer.php) показывает, как использовать функции РНР для работы с регулярными выражениям с целью получения информации по запросам. Программа определяет тип и версию браузера и операционной системы, после чего выводит полученную информацию в окне браузера. Но прежде чем переходить к непосредственному анализу программы, я хочу представить один из главных ее компонентов - стандартную переменную РНР $HTTP_USER_AGENT. В этой переменной в строковом формате хранятся различные сведения о браузере и операционной системе пользователя - именно то, что нас интересует. Эту информацию можно легко вывести на экран всего одной командой:

При работе в Internet Explorer 5.0 на компьютере с Windows 98 результат будет выглядеть так:
Mozilla/4.0 (compatible; MSIE 5.0; Windows 98; DigExt)

Для Netscape Navigator 4.75 выводятся следующие данные:
Mozilla/4.75 (Win98; U)

Sniffer.php извлекает необходимые данные из $HTTP_USER_AGENT при помощи функций обработки строк и регулярных выражений. Алгоритм программы на псевдокоде:

  • Определить две функции для идентификации браузера и операционной системы: browser_info( ) и opsys_info( ). Начнем с псевдокода функции browser_info( ).
  • Определить тип браузера, используя функцию егед( ). Хотя эта функция работает медленнее упрощенных строковых функций типа strstr( ), в данном случае она удобнее, поскольку регулярное выражение позволяет определить версию браузера.
  • Воспользоваться конструкцией if/elseif для идентификации следующих браузеров и их версий: Internet Explorer, Opera, Netscape и браузер неизвестного типа.
  • Вернуть информацию о типе и версии браузера в виде массива.
  • Функция opsys_info( ) определяет тип операционной системы. На этот раз используется функция strstr( ), поскольку тип ОС определяется и без применения регулярных выражений.
  • Воспользоваться конструкцией if/elseif для идентификации следующих систем: Windows, Linux, UNIX, Macintosh и неизвестная операционная система.
  • Вернуть информацию об операционной системе.

Идентификация типа браузера и операционной системы клиента

// Функция: browser_info
// Назначение: Возвращает тип и версию браузера
function browser_info ( $agent ) <
// Определить тип браузера
// Искать сигнатуру Internet Explorer
if ( ereg ( 'MSIE ([0-9].[0-9]<1,2>)' , $agent , $version )) <
$browse_type = "IE" ;
$browse version = $version [ 1 ];
>
// Искать сигнатуру Opera
elseif ( ereg ( 'Opera ([0-9].[0-9]<1,2>)' . $agent , $version )) <
$browse_type = "Opera" :
$browse_version = $version [ 1 ]
>
// Искать сигнатуру Netscape. Проверка браузера Netscape
// *должна* выполняться после проверки Internet Explorer и Opera,
// поскольку все эти браузеры любят сообщать имя
// Mozilla вместе с настоящим именем.
elseif ( ereg ( 'Mozilla/([0-9].[0-9]<1,2>)' . $agent , $version )) <
$browse_type = "Netscape" ;
$browse_version = $version [ 1 ];
>
// Если это не Internet Explorer, Opera или Netscape.
// значит, мы обнаружили неизвестный браузер,
else <
$browse_type = "Unknown" ;
$browse_version = "Unknown" ;
>

// Вернуть тип и версию браузера в виде массива
return array ( $browse_type , $browse_version );
> // Конец функции browser_info

// Функция: opsys_info
// Назначение: Возвращает информацию об операционной системе пользователя
function opsys_info ( $agent ) <
// Идентифицировать операционную систему
// Искать сигнатуру Windows
if ( strstr ( $agent . 'win' ) )
$opsys = "windows" ;
// Искать сигнатуру Linux
elseif ( strstr ( $agent , 'Linux' ) )
$opsys = "Linux" ;
// Искать сигнатуру UNIX
elseif ( strstr ( Sagent , 'Unix' ) )
$opsys = "Unix" ;
// Искать сигнатуру Macintosh
elseif ( strstr ( $agent , 'Mac' ) )
$opsys = "Macintosh" ;
// Неизвестная платформа
else
$opsys = "Unknown" ;
// Вернуть информацию об операционной системе
return $opsys ;
> // Конец функции opsys_info

// Сохранить возвращаемый массив в списке
list ( $browse_type , $browse_version ) = browser_info ( $HTTP_USER_AGENT );
$operating_sys = opsys_info ( $HTTP_USER_AGENT );

print "Browser Type: " . $browse_type . "
" ;
print "Browser Version: " . $browse_version . "
" ;
print "Operating System: " . $operating_sys . "
" :
?>

Модификаторы шаблонов

Ниже перечислены все доступные на сегодняшний день модификаторы. Имя, взятое в круглые скобки, указывает внутреннее PCRE-имя для данного модификатора. Пробелы и переводы строк в модификаторах игнорируются, другие символы вызывают ошибки.

i (PCRE_CASELESS) Если этот модификатор используется, символы в шаблоне соответствуют символам как верхнего, так и нижнего регистра. m (PCRE_MULTILINE) По умолчанию PCRE обрабатывает данные как однострочную символьную строку (даже если она содержит несколько разделителей строк). Метасимвол начала строки '^' соответствует только началу обрабатываемого текста, в то время как метасимвол "конец строки" '$' соответствует концу текста, либо позиции перед завершающим текст переводом строки (в случае, если модификатор D не установлен). В Perl ситуация полностью аналогична. Если этот модификатор используется, метасимволы "начало строки" и "конец строки" также соответствуют позициям перед произвольным символом перевода и строки и, соответственно, после, как и в самом начале и в самом конце строки. Это соответствует Perl-модификатору /m. В случае, если обрабатываемый текст не содержит символов перевода строки, либо шаблон не содержит метасимволов '^' или '$', данный модификатор не имеет никакого эффекта. s (PCRE_DOTALL) Если данный модификатор используется, метасимвол "точка" в шаблоне соответствует всем символам, включая перевод строк. Без него - все символы, кроме переводов строк. Этот модификатор эквивалентен записи /s в Perl. Класс символов, построенный на отрицании, например [^a], всегда соответствует переводу строки, независимо от наличия этого модификатора. x (PCRE_EXTENDED) Если используется данный модификатор, неэкранированные пробелы, символы табуляции и пустой строки будут проигнорированы в шаблоне, если они не являются частью символьного класса. Также игнорируются все символы между неэкранированным символом '#' (если он не является частью символьного класса) и символом перевода строки (включая сами символы '\n' и '#'). Это эквивалентно Perl-модификатору /x, и позволяет размещать комментарий в сложных шаблонах. Замечание: это касается только символьных данных. Пробельные символы не фигурируют в служебных символьных последовательностях, к примеру, в последовательности '(?(', открывающей условную подмаску. e (PREG_REPLACE_EVAL)

Эта функциональность считается УСТАРЕВШЕЙ с PHP 5.5.0, и УДАЛЕНА в PHP 7.0.0.

Прежде чем подставить значение обратной ссылки, к этому значению применяется функция addslashes. Таким образом, если обратная ссылка используется как строка в кавычках, то экранированные символы будут преобразованы в строковые константы. Однако проэкранированные символы, которые обычно не должны изменяться, сохраняют примененные к ним слеши. Это делает использование данного модификатора довольно проблематичным.

Убедитесь, что параметр replacement содержит строку с корректным PHP-кодом, иначе PHP сообщит об ошибке парсинга на строке, содержащей вызов preg_replace() .

Использовать этот модификатор не рекомендуется, так как это может легко добавить уязвимости в системе безопасности:

// Заголовки в верхнем регистре
$html = preg_replace (
'( (.*?) )e' ,
'" " . strtoupper("$2") . " "' ,
$html
);

Приведенный выше код может быть легко скомпрометирован путем передачи такой строки как

Для предотвращения этого типа уязвимости с удаленным исполнением кода следует использовать функцию preg_replace_callback() :

// Заголовки в верхнем регистре
$html = preg_replace_callback (
'( (.*?) )' ,
function ( $m ) <
return " $m [ 1 ] >" . strtoupper ( $m [ 2 ]) . " $m [ 1 ] >" ;
>,
$html
);

Этот модификатор используется только в функции preg_replace() , в других PCRE функциях он игнорируется.

В нежадном режиме обычно невозможно совпадение символов превышающих 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); ранее они считались допустимыми.

User Contributed Notes 11 notes

Regarding the validity of a UTF-8 string when using the /u pattern modifier, some things to be aware of;

1. If the pattern itself contains an invalid UTF-8 character, you get an error (as mentioned in the docs above - "UTF-8 validity of the pattern is checked since PHP 4.3.5"

2. When the subject string contains invalid UTF-8 sequences / codepoints, it basically result in a "quiet death" for the preg_* functions, where nothing is matched but without indication that the string is invalid UTF-8

3. PCRE regards five and six octet UTF-8 character sequences as valid (both in patterns and the subject string) but these are not supported in Unicode ( see section 5.9 "Character Encoding" of the "Secure Programming for Linux and Unix HOWTO" - can be found at http://www.tldp.org/ and other places )

4. For an example algorithm in PHP which tests the validity of a UTF-8 string (and discards five / six octet sequences) head to: http://hsivonen.iki.fi/php-utf8/

The following script should give you an idea of what works and what doesn't;

= array(
'Valid ASCII' => "a" ,
'Valid 2 Octet Sequence' => "\xc3\xb1" ,
'Invalid 2 Octet Sequence' => "\xc3\x28" ,
'Invalid Sequence Identifier' => "\xa0\xa1" ,
'Valid 3 Octet Sequence' => "\xe2\x82\xa1" ,
'Invalid 3 Octet Sequence (in 2nd Octet)' => "\xe2\x28\xa1" ,
'Invalid 3 Octet Sequence (in 3rd Octet)' => "\xe2\x82\x28" ,

'Valid 4 Octet Sequence' => "\xf0\x90\x8c\xbc" ,
'Invalid 4 Octet Sequence (in 2nd Octet)' => "\xf0\x28\x8c\xbc" ,
'Invalid 4 Octet Sequence (in 3rd Octet)' => "\xf0\x90\x28\xbc" ,
'Invalid 4 Octet Sequence (in 4th Octet)' => "\xf0\x28\x8c\x28" ,
'Valid 5 Octet Sequence (but not Unicode!)' => "\xf8\xa1\xa1\xa1\xa1" ,
'Valid 6 Octet Sequence (but not Unicode!)' => "\xfc\xa1\xa1\xa1\xa1\xa1" ,
);

echo "++Invalid UTF-8 in pattern\n" ;
foreach ( $examples as $name => $str ) <
echo " $name \n" ;
preg_match ( "/" . $str . "/u" , 'Testing' );
>

echo "++ preg_match() examples\n" ;
foreach ( $examples as $name => $str ) <

preg_match ( "/\xf8\xa1\xa1\xa1\xa1/u" , $str , $ar );
echo " $name : " ;

if ( count ( $ar ) == 0 ) <
echo "Matched nothing!\n" ;
> else <
echo "Matched < $ar [ 0 ]>\n" ;
>

echo "++ preg_match_all() examples\n" ;
foreach ( $examples as $name => $str ) <
preg_match_all ( '/./u' , $str , $ar );
echo " $name : " ;

$num_utf8_chars = count ( $ar [ 0 ]);
if ( $num_utf8_chars == 0 ) <
echo "Matched nothing!\n" ;
> else <
echo "Matched $num_utf8_chars character\n" ;
>

Пример использования Perl REGEXP для быстрой обработки текста

Весьма полезный рецепт, который облегчает общение с коммандной строкой описан здесь, однако попробовать его вживую не удалось, т.к. под мою систему (OpenIndiana) не существует компиляторя языка Go. Так возникла идея переписать указанную программу на более универсальный язык, который точно существует на любой платформе — Perl.

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

Реализация

Для начала преобразуем переданные функции подсказки в часть будущего регулярного выражения:

Здесь $hints — строка, склеенная из всех подсказок, например, 'abcd'.
Выражение для поиска (.) — это одиночный символ (каждый, учитывая параметр поиска 'g'), заменяем его на себя же ($1 — значение из первых скобок выражения поиска) и дописываем необходимые нам части, а именно:

После каждого символа добавляем блок: '.*?', что означает: любой символ, ноль или более раз, и маркер, который делает модификатор «не жадным» (об этом чуть ниже).

Итого на выходе получаем строку: 'a.*?b.*?c.*?d.*?'

Переходим к основной части, которая осуществляет сравнение строки из «знакомых» папок с подсказкой, условие:

Здесь символ '^' — это «якорь» начала строки, выражение в первых скобках '(.*)' — префикс строки, а вслед за этим выражением — наш подготовленный заранее regexp, содержащий подсказки для поиска, заканчивается выражение вторым «якорем» — '$', который означает совпадение с концом строки.

Поскольку все модификаторы '*' в строке кроме первого, содержат маркеры '?', единственный модификатор без этого маркера становится «жадным», т.е. старается отобрать себе как можно большую часть строки.

В нашем примере, имеем на выходе преобразованную строку: /^(.*)(a.*?b.*?c.*?d.*?)$/

Фактически, поиск в этом случае осуществляется справа налево, т.е. сначала от конца строки ищем ближайший символ 'd', потом слева от него — ближайший символ 'c', затем — ближайший к нему слева символ 'b' и затем — ближайший к нему вимвол 'a', все что будет слева от найденного смвола 'a' попадет в «жадный» префикс. Положение найденного результата в строке мы определим по длине этого префикса, а именно строкой $w = length($1); (здесь $1 получит значение из первых скобок предыдущего regexp), остальные условия (чем правее, чем лучше) за нас уже выполнил сам regexp.

Остается только дописать функции LoadPaths и add и добисать обработку параметров запуска.

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

Функции сравнения строк и поиска подстрок, рассмотренные в предыдущей статье, позволяют успешно справляться с теми задачами, для которых они предназначены, но имеют существенные ограничения в том, что основаны на использовании литеральных значений. В качестве иллюстрации того, в чем состоят ограничения этих функций, рассмотрим следующий пример. Предположим, требуется выполнить проверку строк для определения того, относятся ли эти строки к конкретному типу имени хоста веб-сайта. Адреса, представленные этим именем, должны начинаться с подстроки www., оканчиваться подстрокой .com и иметь в середине одно слово, состоящее из прописных букв. В качестве примера ниже приведены строки, имеющие требуемый формат:

А эти строки не соответствуют указанным требованиям:

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

Для сокращенного обозначения регулярных выражений в английском языке применяется термин . Регулярные выражения представляют собой шаблоны для согласования со строками, содержащие специальные подстановочные символы, которые могут быть согласованы с целыми фрагментами целевой строки. Язык PHP позволяет применять два широких класса регулярных выражений: регулярные выражения, совместимые со стандартом POSIX (называемые расширенными), и обычные регулярные выражения, совместимые с языком Perl. Различия между этими классами в основном касаются синтаксиса, но имеются также некоторые функциональные различия.

В конечном итоге регулярные выражения в стиле POSIX явились итогом развития средств сопоставления с шаблонами регулярных выражений, используемых в интерпретаторах Unix с интерфейсом командной строки, а регулярные выражения, совместимые с языком Perl, в большей степени напоминают соответствующие средства указанного языка. Любой разработчик, которому предстоит выполнить хоть сколько-нибудь существенный объем программирования в среде веб, рано или поздно будет вынужден воспользоваться регулярными выражениями.

Начиная с версии PHP 5.3.0 функции для работы с регулярными выражениями в стиле POSIX являются устаревшими, поэтому далее мы будем рассматривать регулярные выражения в стиле Perl.

Регулярные выражения, совместимые с языком Perl

Для обработки регулярных выражений, совместимых с языком Perl, в языке PHP применяется полностью отдельный набор функций, а формирование шаблонов осуществляется следующим образом:

Шаблоны регулярных выражений, совместимых с языком Perl, всегда начинаются и оканчиваются одним конкретным символом, который должен быть одинаковым и в начале и в конце. Этот символ отмечает начало и конец шаблона. В соответствии с общепринятым соглашением для этой цели чаще всего используется символ /, но при желании может также применяться другой символ. Следующий шаблон, совместимый с языком Perl: /pattern/, согласуется с любой строкой, которая содержит в себе строку (вернее, подстроку) "pattern".

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

Специальный символ ^ согласуется только с началом строки, а специальный символ $ — только с концом строки.

Специальный символ . согласуется с любым символом.

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

Набор символов, заключенный в квадратные скобки, согласуется с любым из символов этого набора. Например, шаблон [ab] согласуется и с буквой a, и с буквой b. В квадратных скобках можно также определить диапазон символов с использованием дефиса. Например, шаблон [a-c] согласуется с буквой a, b или c.

Специальные символы, отмеченные знаком переключения режима обработки в виде обратной косой черты (\), теряют свое специальное значение и сопоставляются непосредственно, как обычные символы.

Если рассматриваемый символ не является специальным, то шаблон в стиле Perl последовательно согласует символы. Например, шаблон /abc/ согласуется с любой строкой, которая содержит подстроку "abc"

Любой шаблон, за которым следует символ ? (вопросительный знак), означает: "Выполнить согласование конструкции, предшествующей этому символу, нуль или один раз".

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

Круглые скобки, выделяющие любую часть шаблона, означают: "Добавить подстроку, которая согласуется с этим подвыражением, к списку подстрок, согласованных с шаблоном"

Рассмотрим в качестве примера следующий шаблон:

Он согласуется с любой строкой, содержащей литеральное выражение "тел.", за которым следует какое-то количество пробелов (но не меньше одного), после чего приведено точно семь цифр (без пробелов и дефисов). Кроме того, в этом шаблоне заданы круглые скобки, поэтому номер, состоящий из семи цифр, сохраняется и возвращается в виде массива, содержащего подстроки сопоставления, если данный шаблон вызывается из функции, которая возвращает подобные объекты.

В следующей таблице представлены все данные по использованию специальных символов в регулярных выражениях:

Специальные символы регулярных выражений PHP

Классы символов
[. ] Любой из символов, указанных в скобках
[^. ] Любой из символов, не указанных в скобках
. Любой символ, кроме перевода строки или другого разделителя Unicode-строки
\w Любой символ, образующий "слово"
\W Любой символ, не являющийся текстовым символом
\s Любой пробельный символ из набора Unicode
\S Любой непробельный символ из набора Unicode. Обратите внимание, что символы \w и \S - это не одно и то же
\d Любые ASCII-цифры. Эквивалентно [0-9]
\D Любой символ, отличный от ASCII-цифр. Эквивалентно [^0-9]
Символы повторения
Соответствует предшествующему шаблону, повторенному не менее n и не более m раз
Соответствует предшествующему шаблону, повторенному n или более раз
Соответствует в точности n экземплярам предшествующего шаблона
? Соответствует нулю или одному экземпляру предшествующего шаблона; предшествующий шаблон является необязательным. Эквивалентно
+ Соответствует одному или более экземплярам предшествующего шаблона. Эквивалентно
* Соответствует нулю или более экземплярам предшествующего шаблона. Эквивалентно
Символы регулярных выражений выбора
| Соответствует либо подвыражению слева, либо подвыражению справа (аналог логической операции ИЛИ).
(. ) Группировка. Группирует элементы в единое целое, которое может использоваться с символами *, +, ?, | и т.п. Также запоминает символы, соответствующие этой группе для использования в последующих ссылках.
(. ) Только группировка. Группирует элементы в единое целое, но не запоминает символы, соответствующие этой группе.
Якорные символы регулярных выражений
^ Соответствует началу строкового выражения или началу строки при многострочном поиске.
$ Соответствует концу строкового выражения или концу строки при многострочном поиске.
\b Соответствует границе слова, т.е. соответствует позиции между символом \w и символом \W или между символом \w и началом или концом строки.
\B Соответствует позиции, не являющейся границей слов.
(?=p) Позитивная опережающая проверка на последующие символы. Требует, чтобы последующие символы соответствовали шаблону p, но не включает эти символы в найденную строку.
(?!p) Негативная опережающая проверка на последующие символы. Требует, чтобы следующие символы не соответствовали шаблону p.
\A начало данных (независимо от многострочного режима)
\Z конец данных либо позиция перед последним переводом строки (независимо от многострочного режима)
Флаги (указываются в конце рег. выражения)
i Указывает, что поиск по шаблону должен быть нечувствителен к регистру символов.
m Многострочный режим поиска (multiline).
s Если данный модификатор используется, метасимвол "точка" в шаблоне соответствует всем символам, включая перевод строк.
x Если используется данный модификатор, неэкранированные пробелы, символы табуляции и пустой строки будут проигнорированы в шаблоне, если они не являются частью символьного класса.

Общие сведения о функциях, совместимых с языком Perl, приведены в таблице ниже:

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

Принимает в качестве первого параметра шаблон регулярного выражения, второго параметра — строку, с которой должно быть выполнено согласование, и необязательного третьего параметра — переменную типа "массив" для возвращаемых результатов согласования. Возвращает 0, если согласование не найдено, и 1, если согласование найдено.

В случае успешного согласования переменная типа "массив" содержит следующие элементы: первым элементом является вся согласованная подстрока, а последующие элементы содержат части, соответствующие заключенным в круглые скобки подвыражениям шаблона. Начиная с версии PHP 4.3.0 предусмотрена также возможность задать необязательный флаг PREG_OFFSET_CAPTURE. Этот флаг вынуждает функцию preg_match() возвращать в указанном массиве в расчете на каждое согласование по одному двухэлементному массиву, состоящему из самих результатов согласования и информации о смещении, с которого было начато успешное согласование.

Аналогична preg_match(), за исключением того, что осуществляет все возможные подряд идущие согласования шаблона со строкой, а не только первое согласование. Возвращаемым значением является количество успешно выполненных согласований. В этой функции третий параметр, массив согласований, является обязательным (если требуется получение только истинного или ложного значения, которое показывает наличие или отсутствие согласования, используйте функцию preg_match()). Структура возвращаемого массива зависит от необязательного четвертого параметра (в качестве этого параметра служит константа PREG_PATTERN_ORDER или PREG_SET_ORDER, причем по умолчанию применяется первая). При вызове этой функции можно также применять константу PREG_OFFSET_CAPTURE (эти константы описываются далее).

Принимает в качестве первого параметра шаблон регулярного выражения и второго параметра — строку, с которой должно быть выполнено согласование. Возвращает массив, содержащий результаты разбиения строки на подстроки в местах вхождения разграничительных строк, согласующихся с шаблоном. Необязательный третий параметр (с обозначением предельного количества) указывает, на какое количество подстрок должна быть разбита строка, прежде чем будет возвращен полученный список; значение -1 в позиции третьего параметра указывает на то, что предел не устанавливается. В качестве необязательного четвертого параметра может быть задан флаг PREG_SPLIT_NO_EMPTY (вынуждающий функцию возвращать только непустые части), PREG_SPLIT_DELIM_CAPTURE (которое требует возврата всех подстрок, соответствующих заключенным в круглые скобки выражениям в шаблоне разграничителя) или PREG_OFFSET_CAPTURE.

Функция Описание
preg_match()
preg_match_all()
preg_split()
preg_replace() Принимает в качестве параметров шаблон, строку замены и строку, в которой должны быть внесены изменения. Возвращает модифицированную строку, в которой вместо каждой подстроки, согласованной с шаблоном, подставлена строка замены. Необязательный параметр с обозначением предельного количества определяет, сколько должно быть выполнено замен (как в функции preg_split())
preg_replace_callback() Эта функция аналогична функции preg_repiace(), за исключением того, что вторым параметром является имя функции обратного вызова, а не строка замены. Эта функция должна возвращать строки, предназначенные для использования в качестве замены
preg_grep() Принимает в качестве параметров шаблон и массив и возвращает массив, состоящий из элементов входного массива, который сопоставляется с шаблоном. Значения, перешедшие из старого массива в новый массив, имеют такие же ключи, как и соответствующие элементы исходного массива
preg_quote() Функция специального назначения, предназначенная для вставки знаков переключения на другой режим обработки в строки, которые предназначены для использования в качестве шаблонов регулярных выражений. Для этой функции требуется единственный параметр—строка, в которую должны быть вставлены знаки переключения; возвращаемым значением является та же строка, в которой перед каждым специальным символом регулярного выражения вставлена обратная косая черта (знак переключения)

По-видимому, наиболее широко используемыми из этих функций являются preg_match() и preg_match_all(). Первая функция представляет собой наилучшее средство получения ответа на простой вопрос о том, сопоставляется ли некоторый шаблон с некоторой строкой, а последняя функция является удобным средством подсчета количества согласований или получения сразу всех согласующихся подстрок.

Необязательный четвертый параметр функции preg_match_all() требует дополнительных пояснений. Как правило, массив, который содержит возвращаемые результаты согласования, имеет двухуровневую структуру. На первом уровне показана очередность сопоставлений (первое сопоставление, второе и т.д.), а на втором показана позиция сопоставления в шаблоне. (Все сопоставление всегда находится на первом месте, а за ним следуют по порядку все субшаблоны, обозначенные круглыми скобками.) Вопрос заключается в следующем: "Какой уровень является самым верхним? Будет ли возвращаемый массив представлять собой список элементов с указанием позиций, каждый из которых содержит список номеров последовательных согласований, или данный массив имеет обратную структуру?"

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

Давайте рассмотрим сначала простой пример, в котором извлекаются все телефонные номера из подстроки и выбирается код страны, код города и номер:

Код PHP "; // Шаблон регулярного выражения $pattern = '/(\d+)[\s|\(|-]*([\d]<3,>)[\s|\)|-]*([\d|-]+)/'; preg_match_all($pattern, $str, $arr, PREG_SET_ORDER); foreach ($arr as $entry) < echo "

Этот код даст следующий результат:

Использование регулярных выражений для поиска телефонных номеров

Я могу долго объяснять принцип работы представленного в примере регулярного выражения, но я лучше покажу:

Схема использования регулярного выражения

Пример: программа извлечения ссылок

В качестве более сложного примера того, какие действия могут быть выполнены с помощью регулярных выражений, рассмотрим функцию, позволяющую извлекать ссылки из текста произвольной веб-страницы и выводить полученные результаты на экран. Входными данными служит URL страницы, которая должна быть проанализирована, а выходными данными являются выведенный на экран список ссылок, представленных на соответствующей странице. Этот список состоит из целевого URL для данной ссылки и описательного текста, присутствующего в этой ссылке (который принято называть анкором (или якорем ссылки)). Для решения указанной задачи используются функции для работы с регулярными выражениями, совместимыми с языком Perl.

Разработка такой функции может стать самым первым шагом при создании веб-навигатора для поисковой машины. Поисковики (Google, Yandex и т.п.) загружают содержимое веб-страниц для их анализа и индексации. В ходе работы возникает необходимость распознавать ссылки на другие страницы, если новое информационное наполнение можно будет найти только на этих страницах.

Основное регулярное выражение

Основой разрабатываемой небольшой функции должно служить само регулярное выражение. Необходимо прежде всего разработать такое регулярное выражение, которое сопоставляется только со ссылками HTML (и ничем иным) и может применяться для извлечения фрагментов таких ссылок. Ссылки HTML, как правило, выглядят таким образом:

Это означает, что ссылка задана дескриптором анкора, который имеет атрибут href, а между начальным дескриптором () и конечным дескриптором () заключен текст анкора. В данном разделе будет создан шаблон, предназначенный для сопоставления со ссылкой такого упрощенного типа. (Разрабатываемый шаблон не позволяет обрабатывать все данные, которые согласно спецификации языка HTML разрешено использовать в качестве допустимых анкорных ссылок. В частности, в анкорах разрешены и другие атрибуты, кроме href, но при решении рассматриваемой задачи такая возможность игнорируется.)

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

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

Обратите внимание на то, что разрабатываемый шаблон еще не предназначен для использования в работоспособном коде PHP; в данном случае создается черновой вариант выражения, которое должно быть вставлено в код PHP позднее. На обычном языке первое черновое определение дескриптора анкора можно описать как состоящее из левой угловой скобки, за которой следует буква "a", затем — пробел, вслед за ним — строка "href=", знак двойной кавычки, произвольное количество символов, отличных от знаков кавычки, заключительный знак кавычки и, наконец, правая угловая скоба. После его составления все выражение заключается в пару символов косой черты, которые служат для машины обработки регулярных выражений указанием на то, где начинается и оканчивается выражение.

Конструкция [^"]+ в середине этого выражения означает примерно следующее: квадратные скобки указывают применяемый набор символов, а знак вставки (^), который непосредственно следует за левой скобкой, указывает, что используется отрицание этого множества символов. Таковым является множество, содержащее все символы, не находящиеся в последующем списке. Наконец, знак + после обозначения класса символов, заключенного в квадратные скобки, показывает, что подразумевается наличие в шаблоне по меньшей мере одного символа, отличного от кавычки.

Как уже было сказано, мы не ставили перед собой задачу точно отразить синтаксис, предписанный спецификацией HTML. Однако существует целый ряд способов, с помощью которых можно немного ослабить ограничения, налагаемые этим выражением. Прежде всего, насколько известно, количество пробелов между начальным символом Код PHP

В качестве текста анкора допускается использовать любой текст, который занимает на странице место вплоть до заключительного дескриптора анкора, поэтому создается класс символов, который включает любые символы, кроме правой угловой скобой ([^>]*), и указывает, что данные символы могут присутствовать в количестве от нуля и больше. Наконец, вводится субшаблон для сопоставления с закрывающим дескриптором анкора ( ).

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

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

Разрабатываемый шаблон к этому времени уже стал намного более сложным для восприятия, но суть изменения состоит в том, что введена пара круглых скобок, которые заключают в себе целевое значение атрибута href (находящееся между кавычками) и еще одну пару вокруг области текста анкора (между дескрипторами). Эти круглые скобки служат для вызывающей функции указанием на то, что необходимо сохранить части строки, сопоставляемые с подвыражениями, заключенными в круглые скобки, чтобы соответствующие подстроки могли быть введены в возвращаемый массив.

Использование данного выражения в функции

После получения необходимого выражения для сопоставления с дескриптором анкора можно приступить к решению следующей задачи — написанию функции, извлекающей ссылки с HTML-страницы. Для этого необходимо осуществить описанные ниже действия:

Принять в качестве параметра заданный URL.

Открыть соединение HTTP с помощью указанного URL и получить информационное наполнение страницы в виде строки.

Выполнить обработку строки в цикле, применяя рассматриваемый шаблон регулярного выражения и сохраняя результаты согласования.

Вывести на экран все пары извлеченных подстрок (состоящие из целевого URL и текста анкора).

В примере ниже показана полная структура целевой страницы включая функцию print_links():

Простая программа поиска ссылок на PHP

Задача разработки этой функции проще, чем кажется на первый взгляд, поскольку в языке PHP уже предусмотрены многие ее составляющие. В частности, не требуется предусматривать каких-либо особых действий по созданию соединения HTTP для загрузки веб-страницы, поскольку функция fopen() принимает URL в качестве параметра и выполняет все необходимое. Поэтому достаточно после вызова функции fopen() с указанным в ней URL считывать символы до тех пор, пока поступление этих символов не прекратится, добавляя полученные данные к создаваемой строке.

Обработка в цикле содержимого HTML-страницы обеспечивается с помощью функции preg_match_all(). В этой функции шаблон регулярного выражения применяется максимально возможное количество раз, после каждого использования обработка строки начинается непосредственно вслед за тем участком, где перед этим было обнаружено совпадение с шаблоном, и результаты поиска сохраняются в массиве $match_array. Здесь мы решили применить структуру массива, упорядоченную в соответствии с флажком PREG_SET_ORDER. Это означает, что каждая запись в массиве верхнего уровня представляет собой часть, соответствующую результатам конкретного согласования с шаблоном в текущей итерации, а не часть, полученную в результате всех согласований.

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

Синтакcис шаблонов, используемых в функциях этого раздела, во многом похож на синтаксис, используемый в Perl. Выражение должно быть заключено в ограничители, например, прямые слеши '/'. Ограничителем могут выступать произвольные символы, кроме буквенно-цифровых и обратного слеша '\'. Если ограничительный символ встречается в шаблоне, его необходимо экранировать. Начиная с PHP 4.0.4 в качестве ограничителя доступны комбинации, используемые в Perl: (), <>, [] и <>.

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

Поддержка Perl-совместимых регулярных выражений реализована в соответствующей PCRE библиотеке, которая распространяется с открытым исходным кодом.

Начиная с PHP 4.2.0, Perl-совместисмые регулярные выражения (PCRE) доступны по умолчанию. Вы можете отключить их при помощи --without-pcre-regex. В случае, если вы хотите использовать библиотеку, отличную от идущей в стандартной поставке РНР, используйте опцию --with-pcre-regex=DIR для указания директории, содержащей необходимые файлы. Если у вас версия PHP менее, чем 4.2.0, вам необходимо сконфигурировать и пересобрать PHP с опцией --with-pcre-regex[=DIR], чтобы включить поддержку PCRE-функций.

Версия PHP для Windows имеет встроенную поддержку данного расширения. Это означает, что для использования данных функций не требуется загрузка никаких дополнительных расширений.

Перечисленные ниже константы определены данным расширением и могут быть доступны только в том случае, если PHP был собран с поддержкой этого расширения или же в том случае, если данное расширение подгружается во время выполнения.

  • PREG_PATTERN_ORDER Меняет порядок элементов в результирующем массиве так, чтобы элемент $matches[0] содержал полные вхождения шаблона, элемент $matches[1] - все вхождения первой взятой в круглые скобки подмаски, и так далее. Только reg_match_all() реагирует на данный модификатор; остальными функциями он игнорируется.
  • PREG_SET_ORDER Меняет порядок элементов в результирующем массиве так, чтобы элемент $matches[0] содержал первый набор вхождений (полное вхождение, вхождение первой подмаски, заключенной в круглые скобки. ), аналогично элемент $matches[1] - второй набор вхождений, и так далее. Только reg_match_all() реагирует на данный модификатор; остальными функциями он игнорируется.
  • PREG_OFFSET_CAPTURE Смотрите описание флага PREG_SPLIT_OFFSET_CAPTURE. Данный флаг доступен в PHP 4.3.0 и выше.
  • PREG_SPLIT_NO_EMPTY В случае, если этот флаг указан, функция preg_split() вернет только непустые подстроки.
  • PREG_SPLIT_DELIM_CAPTURE В случае, если этот флаг указан, выражение, заключенное в круглые скобки в разделяющем шаблоне, также извлекается из заданной строки и возвращается функцией. Этот флаг был добавлен в PHP 4.0.5.
  • PREG_SPLIT_OFFSET_CAPTURE В случае, если этот флаг указан, для каждой найденной подстроки будет указана ее позиция в исходной строке. Необходимо помнить, что этот флаг меняет формат возвращаемых данных: каждое вхождение возвращается в виде массива, в нулевом элементе которого содержится найденная подстрока, а в первом - смещение. Этот флаг доступен в PHP 4.3.0 и выше и используется только в функции preg_split().

Функция PHP preg_grep

Возвращает массив вхождений, которые соответствуют шаблону

Функция PHP preg_match

Выполняет проверку на соответствие регулярному выражению

Илон Маск рекомендует:  Окна и формы
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL