Основы PHP
  Что такое PHP?
  Возможности PHP
  Преимущества PHP
  История развития
  Что нового в PHP5?
  «Движок» PHP
  Переход на PHP 5.3
New Переход на PHP 5.6
  Введение в PHP
  Изучение PHP
  Основы CGI
  Синтаксис PHP
  Типы данных PHP
  Переменные в PHP
  Константы PHP
  Выражения PHP
  Операторы PHP
  Конструкции PHP
  Ссылки в PHP
  PHP и ООП
  Безопасность
  Функции PHP
  Функции по категориям
  Функции по алфавиту
  Стандартные функции
  Пользовательские
  PHP и HTTP
  Работа с формами
  PHP и Upload
  PHP и Cookies
  PHP и базы данных
  PHP и MySQL
  Документация MySQL
  Учебники
  Учебники по PHP
  Учебники по MySQL
  Другие учебники
  Уроки PHP
  Введение
  Самые основы
  Управление
  Функции
  Документация
  Математика
  Файлы
  Основы SQL
  Дата и время
  CURL
  Изображения
  Стили
  Безопасность
  Установка
  Проектирование БД
  Регулярные выражения
  Подготовка к работе
  Быстрый старт
  Установка PHP
  Установка MySQL
  Конфигурация PHP
  Download / Скачать
  Скачать Apache
  Скачать PHP
  Скачать PECL
  Скачать PEAR
  Скачать MySQL
  Редакторы PHP
  Полезные утилиты
  Документация
  PHP скрипты
  Скачать скрипты
  Инструменты
  PHP в примерах
  Новости портала
 Главная   »  Сборник статей
 
 

Функции регулярных выражений

Автор: Александр Грималовский

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

Функция preg_match()

Синтаксис:

int preg_match (string pattern, string subject [, array matches])

Эта функция предназначена для проверки того, совпадает ли заданная строка (subject) с заданным регулярным выражением (pattern). В качестве результата функция возвращает 1, если совпадения были найдены и 0, если нет. Если при вызове функции был задан необязательный параметр matches, то после работы функции ему будет присвоен массив, содержащий результаты поиска по заданному регулярному выражению. Заметьте, что вне зависимости от того, сколько именно совпадений было найдено при поиске - вам будет возвращено только первое совпадение. Рассмотрим пример того, как это работает:

<?php
$str = "123 234 345 456 567";                       // Строка для поиска
$result = preg_match('/\d{3}/',$str,$found);        // Производим поиск
echo "Matches: $result<br>";                        // Выводим количество найденных совпадений
print_r($found);                                    // Выводим результат поиска
?>

Результатом работы этой программы будет:

Matches: 1

Array
(
    [0] => 123
)

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

<?php
$str = "123 234 345 456 567";
// Теперь мы не просто ищем трехзначное число,
// но и получаем его среднюю цифру
$result = preg_match('/\d(\d)\d/',$str,$found);
echo "Matches: $result<br>";
print_r($found);
?>

Результат будет следующим:

Matches: 1

Array
(
    [0] => 123
    [1] => 2
)

Как видите - здесь присутствуют результаты поиска по всем имеющимся регулярным выражениям.

Функция preg_match_all()

Синтаксис:

int preg_match_all (string pattern, string subject, array matches [, int order])

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

<?php
$str = "123 234 345 456 567";
$result = preg_match_all('/\d{3}/',$str,$found);
echo "Matches: $result<br>";
print_r($found);
?>

Результат работы:

Matches: 5
   Array
   (
       [0] => Array
           (
               [0] => 123
               [1] => 234
               [2] => 345
               [3] => 456
               [4] => 567
           )
)

Как видите - здесь мы получили все найденные совпадения и их количество в качестве результата.

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

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

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

Сначала посмотрим на то, как выглядит результат при использовании PREG_PATTERN_ORDER:

<?php
$str = "123 234 345 456 567";
$order = PREG_PATTERN_ORDER;
$result = preg_match_all('/\d(\d)\d/',$str,$found,$order);
print_r($found);
?>

Результат:

Array
  (
      [0] => Array
          (
              [0] => 123
              [1] => 234
              [2] => 345
              [3] => 456
              [4] => 567
          )
      [1] => Array
          (
              [0] => 2
              [1] => 3
              [2] => 4
              [3] => 5
              [4] => 6
          )
)

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

Теперь попробуем то же самое, но с PREG_SET_ORDER:

<?php
$str = "123 234 345 456 567";
$order = PREG_SET_ORDER;
$result = preg_match_all('/\d(\d)\d/',$str,$found,$order);
print_r($found);
?>

Результат:

Array
 (
     [0] => Array
           (
               [0] => 123
               [1] => 2
           )
     [1] => Array
           (
               [0] => 234
               [1] => 3
           )
     [2] => Array
           (
               [0] => 345
               [1] => 4
           )
     [3] => Array
           (
               [0] => 456
               [1] => 5
           )
     [4] => Array
           (
               [0] => 567
               [1] => 6
           )
)

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

Функция preg_replace()

Синтаксис:

mixed preg_replace (mixed pattern, mixed replacement, mixed subject [, int limit])

Эта функция позволит вам произвести замену текста по регулярному выражению. Как и в предыдущих функциях, здесь производится поиск по регулярному выражению pattern в тексте subject, и каждый найденный фрагмент текста заменяется на текст, заданный в replacement. Задание необязятельного параметра limit позволит ограничить количество заменяемых фрагментов в тексте.

Например, нам необходимо "сжать" текст, убрав из него все лишние пробелы и символы перевода строки:

<?php
$text = "there     is\t\n\t\t some text  \n  \t just  \n\n\n for     test";
echo "<b>Перед заменой:</b>\n$text\n\n";
$text = preg_replace("/(\n \s{2,})/"," ",$text);
echo "<b>После замены:</b>\n$text";
?>

Результатом работы данной программы будет следующий текст:

Перед заменой:
there     is
         some text
     just


 for     test

После замены:
there is some text just for test

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

Однако основная прелесть этой функции, которая и придает ей всю ее мощь - это тот факт, что вы можете ссылаться на результаты поиска при генерации замещающего текста. В качесте примера покажу, как можно очень быстро и элегантно решить задачу, которая возникает достаточно часто - конвертация дат из одного формата в другой. Как вы знаете, на Западе обычно используется формат mm/dd/yyyy, тогда как у нас обычно - dd.mm.yyyy. Следующий пример осуществляет конвертацию дат между этими форматами в заданном тексте:

<?php
$text = 'Today is 11/16/2001';
$text = preg_replace("/(\d{2})\/(\d{2})\/(\d{4})/","\\2.\\1.\\3",$text);
echo $text;
?>

Результат работы этой программы:

Today is 16.11.2001

Обратите внимание на текст, используемый для замены. В нем использованы т.н. backreferences, т.е. ссылки на найденный ранее текст. Всего таких ссылок может быть не более 100 с номерами от 0 до 99 (соответственно в тексте они выглядят как \0, \1, \2 ... \99). Backreference с номером 0 будет заменена на весь найденный текст, \1 - на текст, найденный первым внутренним регулярным выражением, \2 - вторым и т.д. Номерв внутренним регулярным выражениям присваиваются по мере их находжения в тексте, т.е. слева-направо. В нашем случае \1 - это месяц, \2 - день, \3 - год.

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

<?php
$text = "123 234 345 456 567";
$text = preg_replace("/\d+/e","'0x'.dechex('\\0')",$text);
print_r($text);
?>

Результатом работы этой программы будет:

0x7b 0xea 0x159 0x1c8 0x237

И еще одно. Функция preg_replace() также умеет работать с массивами регулярных выражений. Т.е. это позволит вам осуществить поиск и замену сразу по множеству условий! В качестве примера приведу фрагмент кода, описанный в PHP Manual и осуществляющий конвертацию HTML документа в текст при помощи всего лишь одного вызова preg_replace()!

// $document should contain an HTML document.
// This will remove HTML tags, javascript sections
// and white space. It will also convert some
// common HTML entities to their text equivalent.

$search = array ("'<script[^>]*?>.*?</script>'si",  // Strip out javascript
                 "'<[\/\!]*?[^<>]*?>'si",           // Strip out html tags
                 "'([\r\n])[\s]+'",                 // Strip out white space
                 "'&(quot #34);'i",                 // Replace html entities
                 "'&(amp #38);'i",
                 "'&(lt #60);'i",
                 "'&(gt #62);'i",
                 "'&(nbsp #160);'i",
                 "'&(iexcl #161);'i",
                 "'&(cent #162);'i",
                 "'&(pound #163);'i",
                 "'&(copy #169);'i",
                 "'&#(\d+);'e");                    // evaluate as php

$replace = array ("",
                  "",
                  "\\1",
                  "\"",
                  "&",
                  "<",
                  ">",
                  " ",
                  chr(161),
                  chr(162),
                  chr(163),
                  chr(169),
                  "chr(\\1)");

$text = preg_replace ($search, $replace, $document);

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

Функция preg_replace_callback()

Синтаксис:

mixed preg_replace_callback (mixed pattern, mixed callback, mixed subject [, int limit])

Эта функция является расширенной версией функции preg_replace() (хотя, казалось бы, чего еще можно пожелать?). Единственным отличием ее от preg_replace() является то, что в качестве текста для замены в ней задается не сам текст, а имя функции, которая будет производить обработку найденного текста и возвращать замещающий текст. Т.е. с использованием этой функции мощь инструментария PHP по обработке текста становится поистине безграничной! В качестве примера хочу привести фрагмент кода, который выполняет работу, аналогичную той, что производится механизмом сессий в PHP: добавление дополнительного аргумента (идентификатора сессии) к каждой ссылке внутри HTML документа.

<?php
// Список тегов и аттрибутов, к котроым необходимо
// добавить дополнительный параметр.
// Формат строки:
// <имя тега>[ <имя аттрибута>]+
// Т.е. сначала идет имя тега, а затем, через пробел,
// имена одного или нескольких аттрибутов.
$tagsList = array(
    'a href',
    'area href',
    'frame src',
    'input src',
    'img src',
    'form action'
);
// Идентификатор сессии
$sid = 12345;
// HTML документ для обработки. Здесь, в качестве примера
// мы берем его из внешнего файла, но вообще-то метод
// получения исходного документа может быть различным.
$document = join('',file('document.html'));
// Начинаем обработку всех тегов, указанных в массиве $tagsList
foreach($tagsList as $tag)
{
// Разделяем список аттрибутов на составляющие
    $attrs = explode(' ',$tag);
// Получаем имя тега (в массиве $attrs остается лишь список аттрибутов)
    $tag = array_shift($attrs);
// Выполняем "патч" всех имеющихся в документе ссылок, содержащихся
// в каждом из аттрибутов текущего тега
    foreach($attrs as $attr)
        $document = preg_replace_callback("/<".$tag.".+?".$attr."=[\'\"](.+?)[\'\"]/si",
                                          'callback',$document);
};
// Выводим документ и выходим
echo $document;
exit();
// Эта функция будет вызываться для каждой найденной
// ссылки в тексте HTML документа.
// На входе она получает результат поиска (массив,
// аналогичный возвращаемому функцией preg_match()).
// На выходе из функции должна быть строка с текстом замены.
function callback($data)
{
// Регулярное выражение, использованное для поиска находит полные
// HTML теги, содержащие аттрибуты, в которых могут находиться
// URL адреса. Поскольку текст, возвращаемый данной функцией будет
// использован для замещения всего найденного фрагмента текста -
// нам необходимо взять полный текст, чтобы не потерять его при
// дальнейшей обработке. Он будет возвращен без изменений, если
// окажется, что аттрибут не содержит URL адреса.
    $href = $data[0];
// Используем функцию PHP для разбора URL адреса на составляющие.
// В качестве "исходного материала" передаем содержимое интересующего
// нас аттрибута, найденного внутренним регулярным выражением.
// Подробнее о том, что возвращает эта функция см. PHP Manual.
    $parts = parse_url($data[1]);
// Мы должны добвлять идентификатор сессии только к ссылкам, которые
// являются "локальными" для данного сайта. Т.е. мы не должны обрабатывать:
//  - полные URL адреса (<a href="http://www.php.net/">)
//  - указатели на "якоря" внутри страницы (<a href="#part2">)
    if ((!isset($parts['scheme'])) &&   // Если URL содержит идентификатор
        (!isset($parts['host'])) &&     // протокола или имя домена - это
                                        // полный URL адрес.
        (substr($data[1],0,1)!='#'))    // Если URL начинается с символа '#'
                                        // то это ссылка на "якорь" внутри страницы
    {
// Берем путь к странице, указанный в URL и добавляем разделитель для параметров
// потому что нам необходимо будет добавить по крайней мере 1 параметр
        $href = $parts['path'].'?';
// Если в этом URL уже были какие-либо параметры - добавляем их и добавляем
// разделитель. Заметьте, что в качестве разделителя используется &amp;, а не &,
// это позволяет нам добиться совместимости с XHTML.
        if (isset($parts['query']))
            $href .= $parts['query'].'&amp;';
// Добавляем наш собственный параметр - идентификатор сессии
        $href .= 'sid='.$GLOBALS['sid'];
// Если в оригинальном URL была ссылка на фрагмент документа - возвращаем ее
// на место.
        if (isset($parts['fragment']))
            $href .= '#'.$parts['fragment'];
// "Вставляем" новый URL на место того, который был там раньше
        $href = str_replace($data[1],$href,$data[0]);
    };
// Возвращаем результат
    return($href);
};
?>

Пример может показаться немного громоздким, но это исключительно из-за обилия комментариев.

Функция preg_split()

Синтаксис:

array preg_split (string pattern, string subject [, int limit [, int flags]])

Данная функция выполняет действие, аналогичное функциям split() и explode() - разбивает строку на части по какому-либо признаку и возвращает массив, содержащий части строки. Однако ее возможности по заданию правил разбиения больше, чем у этих функций, потому что в ее основе лежит механизм регулярных выражений, в мощи которого, я надеюсь, вы уже смогли убедиться. Если говорить более конкретно, то строка subject разбивается на части по разделителю, заданному регулярным выражением pattern. При этом количество фрагментов может быть ограничего необязятельным параметром limit. Кроме того эта функция поддерживает необязательный параметр flags, который позволяет в некоторой степени контролировать процесс разбиения строки.

Параметр flags может принимать следующие значения (или их комбинации с использованием знака ' '):

  • PREG_SPLIT_NO_EMPTY - возвращать только непустые части строк, полученные в результате разбиения.
  • PREG_SPLIT_DELIM_CAPTURE - возвращать также результаты поиска по внутренним регулярным выражениям.

Рассмотрим пару примеров. Для начала - выражение, которое разбивает произвольный текст на отдельные слова:

<?php
$text = join('',file('my_text.txt'));
$words = preg_split("/\s+/s",$text);
print_r($words);
?>

Как видите - мы получаем содержимое файла 'my_text.txt' в виде строки, разбиваем его на отдельные слова и выводим содержимое массива слов, чтобы убедиться, что все работает правильно.

Еще один пример производит разбиение заданного слова на буквы (он описан в PHP Manual):

<?php
$str = 'string';
$chars = preg_split('//',$str,-1,PREG_SPLIT_NO_EMPTY);
print_r($chars);
?>

Значение -1 для параметра limit означает отсутствие лимита.

Функция preg_quote()

Синтаксис:

string preg_quote (string str [, string delimiter])

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

. \ + * ? [ ^ ] $ ( ) { } = ! < >   :

Все эти символы, встречающиеся в строке будут "отквочены" путем добавления символа '\' непосредственно перед каждым из них. Модифицированная таким образом строка будет возвращены в качестве результата.

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

Функция preg_grep()

Синтаксис:

array preg_grep (string pattern, array input)

Действие этой функции похоже на действие команды grep в Unix. Она ищет текст по регулярному выражению pattern, в массиве input и возвращает новый массив, содержащий только элементы, в которых были найдены совпадения с заданным регулярным выражением. К примеру у нас есть файл, содержащий в каждой строке числовую и текстовую информацию. Нам необходимо получить из этого файла только строки, содержащие числа:

Файл data.txt:

123
abc
php4

Код:

<?php
// Считываем содержимое файла в массив
$data = file('data.txt');
// Получаем массив, содержащий цифровую информацию
$numbers = preg_grep("/\d+/",$data);
// Выводим результат работы 
print_r($numbers);
?>

Результат работы будет:

Array
(
    [0] => 123
    [2] => php4
)

Как видите - мы получили все строки, содержащие цифры. Если же нам, например нужно получить только цифры - то выражение необходимо немного изменить: /^\s*\d+\s*$/.

 
 » Обсудить эту статью на форуме

 
 Сборник статей 
 Содержание раздела 
Есть еще вопросы или что-то непонятно - добро пожаловать на наш  форум портала PHP.SU 
 

 
Powered by PHP  Powered By MySQL  Powered by Nginx  Valid CSS