Регулярные выражения — программируем текстовый поиск

 | 15.21

Мой Компьютер, №07 (511), 01.07.2008

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

Как и многие полезные вещи, регулярные выражения пришли из операционной системы UNIX. Существует их две разновидности — POSIX-совместимые (были разработаны первыми и сейчас считаются устаревшими) и Perl-совместимые (обладают более богатым синтаксисом, считаются более прогрессивными и сейчас наиболее популярны). Соответственно, последние мы и будем рассматривать.

Шаг 1. Начинаем с азов

Какой самый простейший паттерн? Нетрудно догадаться, что это паттерн, описывающий точно заданную строку символов. То есть паттерн abc найдет строку abc. В регулярных выражениях любой символ означает самого себя. Исключение составляют специальные метасимволы ( ^ $ . [ ] | ( ) ? * + { } -), которые, по сути, являются операторами языка регулярных выражений, а также символы-ограничители (/ или < >) паттерна.

Что же делать, если надо искать один из выше перечисленных символов? На помощь приходит наш старый знакомый слэш. Если его поставить перед метасимволом, то метасимвол теряет свой «служебный статус». Ну, а сам трудяга-слэш изображается в виде самого себя, но удвоенного: . Соответственно, для поиска сроки 1+1 паттерн будет 1+1, для поиска строки array[] — array[] и т.д.

Шаг 2. Escape-последовательности

Табл. 1

С алфавитно-цифровыми и служебными символами мы разобрались. Теперь представим ситуацию, когда нам надо найти какой-нибудь непечатный символ — пробел, табуляцию, перенос строки и т.п. Читатели, знакомые с языком C, сразу сообразят, что такие символы изображаются через escape-последовательности. Perl-совместимые регулярные выражения поддерживают следующие последовательности (табл. 1).

Шаг 3. Группы символов и повторения

Итак, мы рассмотрели, как в паттернах задаются конкретные символы и слова. Но, как говорилось, основная сила регулярных выражений в том, что они могут искать некоторую сущность, заданную не определенным текстом, а некоторыми общими характеристиками. Например, мы ищем в тексте номер телефона. Что общего у всех номеров телефона? Формат записи. Каждый номер телефона состоит из кода АТС (3 цифры) и двух двухзначных чисел, разделенных дефисами.

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

Для решения этой задачи нам надо уметь записывать:

  • диапазон значений, который будет стоять на месте одного символа;
  • количество повторений символа.

В Perl-совместимых регулярных выражениях для этого используются специальные метасимволы.

Для выбора из группы символов используются квадратные скобки, в которых и перечисляются возможные значения (без запятых между ними). Например, [abc] означает что символ может принимать значение a, b или c. Если нам надо цифры, то перечень будет следующим: [0123456789]. Наверняка вы скажете, что это слишком громоздкая запись. И будете правы. На самом деле можно записать намного проще, при помощи диапазона: [0-9]. В одной группе можно указывать несколько диапазонов, совмещать диапазоны и обычное перечисление значений: [A-Za-z], [0-9abc]. Какой угодно символ обозначается точкой: . (без квадратных скобок). Существуют также определенные обозначения для указания наиболее часто используемых групп символов (цифры, алфавитно-цифровые и т.д.), а также определенные нюансы в использовании символов – и ], но мы их рассмотрим в следующих статьях.

Для выбора количества повторений используется специальная конструкция метасимволов, именуемая квантификатором (англ. quantity — количество). Квантификатор записывается при помощи фигурных скобок {m,n}, где m — минимальное, а n — максимальное количество повторений символа. Например, запись [0-9]{2,3} означает, что мы ищем последовательность из двух или трех цифр. Верхнюю (но не нижнюю!) границу можно не указывать: [0-9]{2,} будет означать поиск последовательности двух и более цифровых знаков. Если надо найти точное количество символов, то надо указать только одно число без запятой ([0-9]{2} — ровно два цифровых знака).

Для наглядности напишу небольшую табличку квантификаторов (табл. 2).

Табл. 2

Квантификатор {0,1} применяется для задания в паттерне символа или последовательности, которая может присутствовать, а может и не присутствовать в сущности, которую ищем. К примеру, если мы задаем паттерн для расширенной записи телефона с кодом страны и города, то плюс в начале номера, который может быть или не быть, будет обозначаться как +{0,1}.

ВНИМАНИЕ! КВАНТИФИКАТОРОВ ВИДА «ДО m РАЗ» ({,m}) НЕ СУЩЕСТВУЕТ. КОНСТРУКЦИЯ {,m} (где m — некоторое число) БУДЕТ ВОСПРИНЯТА КАК ОБЫЧНАЯ ПОСЛЕДОВАТЕЛЬНОСТЬ СИМВОЛОВ!

Шаг 4. Конструируем регулярные выражения

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

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

Табл. 3

К примеру, для номера телефона — сначала идет код АТС, он состоит из трех цифр, первая из которых не нулевая. Тут две группы символов: один символ диапазона 1–9 и последовательность двух символов диапазона 0–9. На языке регулярных выражений это запишется как [1-9]{1}[0-9]{2}. После кода АТС идет дефис, следовательно, дописываем в паттерн обозначение дефиса (не забываем поставить слэш, т.к. это метасимвол): . И так далее. Подробный процесс создания паттерна для номера телефона приведен в таблице 3.

Шаг 5. Реализация на PHP

Итак, мы создали паттерн для поиска телефонного номера. Теперь вам уже, наверное, не терпится опробовать его на практике :-). Не волнуйтесь, сейчас перейдем к практической работе.

Для чего используются регулярные выражения? Да для тех же целей, что и функции strpos(), substr(), str_replace():

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

Для решения вышеуказанных задач, в PHP существуют функции preg_match(), preg_match_all(), preg_replace(). Сегодня мы кратко рассмотрим первые две из них.

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

Формат вызова:

mixed preg_match ( string $pattern , string $subject );

Если в строке есть хотя бы один участок, соответствующий паттерну, то функция вернет количество совпадений. Если совпадений нет — вернет 0. Если вы допустили в маске ошибку, на выходе функция выдаст FALSE.

Самые нетерпеливые, наверное, уже написали скрипт, ввели в качестве первого параметра наш паттерн, в качестве второго — любой номер телефона, запустили и… функция выдала ошибку :-). Дело в том, что в PHP паттерны необходимо заключать в специальные ограничители. Обычно для этих целей используются пары символов / / или < >. ВНИМАНИЕ! Если символ-ограничитель используется в паттерне, он должен быть «заслэшен»!

Возможности функции демонстрирует скрипт example1.php. Он проверяет, является ли введенное в поле формы значение номером телефона:

<?php

// если в поле формы был введен текст

if (isset($_REQUEST[«phone»]))

 {

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

 if (preg_match(«/[1-9][0-9]{2}-[0-9]{2}-[0-9]{2}/», $_REQUEST[«phone»]))

   // в тексте поля формы имеются совпадения с паттерном — следовательно, в нем содержатся

   // номера телефонов

   $result = «Введенный текст является номером телефона»;

 else

   // соответствий нет — текст не содержит номера телефонов

   $result = «Введенный текст не является номером телефона»;

 }

 

?>

<!—собственно сама HTML-форма —>

<form id=»checkform» name=»checkform» action=»example1.php» method=»post»>

 <table width=»250″>

 <tr>

 <td>

 Номер телефона:

 </td>

 <td>

 <!— поле ввода —>

 <input type=»text» id=»phone» name=»phone» size=»15″ value=»<?php echo $_REQUEST[«phone»]; ?>»>

 </td>

 </tr>

 <tr>

 <td colspan=»2″ align=»center»>

 <input type=»submit» value=»Проверить»>

 </td>

 </tr>

 </table>

</form>

<?php

// если в поле формы был введен текст

if (isset($_REQUEST[«phone»]))

 {

 ?>

 <br>

 <?php

 // выводим результат проверки

 echo $result;

 }

?>

Запускаем скрипт, вводим в поле разные значения и любуемся результатом (рис. 1, 2, 3):

Рис. 1

Рис. 2

Рис. 3

Функция preg_match_all() позволяет извлечь из текста все соответствия паттерном.

Формат вызова:

int preg_match_all ( string $pattern , string $subject , array $&matches);

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

Возможности функции демонстрирует скрипт example2.php. Он обрабатывает текст, введенный в область ввода формы, извлекает из него все телефоны и выводит на печать.

<?php

// если в поле формы был введен текст

if (isset($_REQUEST[«text»]))

 {

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

 // и извлекаем все совпадения

 if (preg_match_all(«/[1-9][0-9]{2}-[0-9]{2}-[0-9]{2}/», $_REQUEST[«text»], $matches))

   {

   // в тексте поля формы имеются совпадения с паттерном — следовательно, в нем содержатся

   // номера телефонов

   // нулевой элемент массива $matches содержит все совпадения.

   // объединяем его элементы через запятую функцией implode() и добавляем

   // к строке результата

   $result = «Во введенном тексте содержатся следующие номера телефонов: «.implode(«, «, $matches[0]);

   }

 else

   // соответствий нет — текс не содержит номера телефонов

   $result = «Во введенном тексте нет номеров телефона»;

 }

 

?>

<!—собственно сама HTML-форма —>

<form id=»checkform» name=»checkform» action=»example2.php» method=»post»>

 <table width=»250″>

 <tr>

 <td>

 Введите текст:

 </td>

 </tr>

 <tr>

 <td>

 <!— поле ввода —>

 <textarea id=»text» name=»text» style=»width:100%;» rows=»5″><?php echo $_REQUEST[«text»]; ?></textarea>

 </td>

 </tr>

 <tr>

 <td align=»center»>

 <input type=»submit» value=»Проверить»>

 </td>

 </tr>

 </table>

</form>

<?php

// если в поле формы был введен текст

if (isset($_REQUEST[«text»]))

 {

 ?>

 <br>

 <?php

 // выводим результат

 echo $result;

 }

?>

Запускаем скрипт, открываем страничку контактов какой-нибудь киевской фирмы, копируем ее содержимое, вставляем в поле ввода и любуемся результатом (рис. 4, 5).

Рис. 4

Рис. 5

Итоги

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

  • шаблон мобильного номера (+380xxyyyyyyy или +38(0xx)yyyyyyy или +38(0xx)yyy yyyy или +38(0xx)yyy-yy-yy);
  • универсальный шаблон телефонного номера (+код_страны(код города)номер). Предусмотрите возможность написания или ненаписания плюса, скобок, в которые заключен код города, и дефисов. Длина кода города не должна ограничиваться тремя символами;
  • шаблон е-мейла (логин@сервер);
  • шаблон URL (http://www.что-то_там) — «www» может либо быть, либо не быть.

Желаю вам успешно решить задания, и до встречи на страницах МК!

Алексей «CyberAdmin» СЕРДЮКОВ

Robo User
Web-droid editor

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *