PHP, MySql, уроки PHP, SEO статьи - Arts-UP.ru

Мини профиль

Регистрация | Забыли?

Наша кнопка

Arts-UP.ru - создание и продвижение

Пользователи онлайн

Пользователи: 
- нет
Гости: 

Роботы: 
- нет
Всего: 1

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

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

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

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

К примеру вот пример выражения для проверки телефонного номера в США:

preg_match("/^(1[-\s.])?(\()?\d{3}(?(2)\))[-\s.]?\d{3}[-\s.]?\d{4}$/",$number)
       

Пример регулярного выражения:
preg_match("/^
    (1[-\s.])?    # выборочно '1-', '1.' or '1'
    ( \( )?        # выборочно: открывающаяся скобка
    \d{3}        # код
    (?(2) \) )    # если была открыта круглая скобка, закрыть
    [-\s.]?        # далее '-' или '.' или пробел
    \d{3}        # первые 3 цифры
    [-\s.]?        # далее '-' или '.' или пробел
    \d{4}        # последние 4 цифры
$/x",$number);



Хитрость заключается в символе “\x” в конце выражения. Это позволяет игнорировать пробелы (исключение: экранированные пробелы при помощи \s) в самом выражении для того, чтобы мы могли использовать комментарии (начинающиеся с # и заканчивающиеся перед переходом на новую строку);
Использование функций обратного вызова

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

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

Пример замены в шаблоне email:
$template = "Здравствуйте, [first_name] [last_name],

Спасибо за покупку [product_name] в магазине [store_name].

Конечная цена [product_price] плюс [ship_price] за доставку.

Вы можете ожидать посылку в течении [ship_days_min], [ship_days_max] рабочих дней.

С уважением,
[store_manager_name]";

// предположим что в массиве $data данные, которые нужно вставить вместо маркеров
// такие как $data['first_name'] $data['product_price'] и т.д.

$template = str_replace("[first_name]",$data['first_name'],$template);
$template = str_replace("[last_name]",$data['last_name'],$template);
$template = str_replace("[store_name]",$data['store_name'],$template);
$template = str_replace("[product_name]",$data['product_name'],$template);
$template = str_replace("[product_price]",$data['product_price'],$template);
$template = str_replace("[ship_price]",$data['ship_price'],$template);
$template = str_replace("[ship_days_min]",$data['ship_days_min'],$template);
$template = str_replace("[ship_days_max]",$data['ship_days_max'],$template);
$template = str_replace("[store_manager_name]",$data['store_manager_name'],$template);

// это можно сделать и в цикле,
// но лучше избежать столького количества итераций


Заметим, что любой из “маркеров”, представленных в выражении выше, заключён в квадратные скобки. Мы можем выследить их и заменить при помощи функции обратного вызова.

Вот так мы и поступим:
// my_callback() будет вызываться каждый раз, когда будут найдены скобки
$template = ppreg_replace_callback('/\[(.*)\]/','my_callback',$template);

function my_callback($matches) {
    // $matches[1] теперь содержит строку, которая находилась между скобок

    if (isset($data[$matches[1]])) {
        // вернуть заменённую строку
        return $data[$matches[1]];
    } else {
        return $matches[0];
    }
}
       

Теперь строка используется только единожды.
“Сетка” против “Не сетки”

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

    $html = 'Привет <a href="http://arts-up.ru">Мир!</a>';
    if (preg_match_all('/<a.*>.*<\/a>/',$html,$matches)) {
        print_r($matches);
    }
     
    Результат будет следующим:
    /* на выходе:
    Array
    (
        [0] => Array
            (
                [0] => <a href="http://arts-up.ru">Мир!</a>
            )
    )
    */


Добавим второй тег:
   
$html = '<a href="http://arts-up.ru">Привет</a>
    <a href="http://arts-up.ru">Мир!</a>';
     
    if (preg_match_all('/<a.*>.*<\/a>/',$html,$matches)) {
        print_r($matches);
    }
     
    /* на выходе:
    Array
    (
        [0] => Array
            (
                [0] => <a href="http://arts-up.ru">Hello</a>
                [1] => <a href="http://arts-up.ru">World!</a>
            )
    )
    */


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

$html = '<a href="http://arts-up.ru">Hello</a> <a href="http://arts-up.ru">World!</a>';
     
    if (preg_match_all('/<a.*>.*<\/a>/',$html,$matches)) {
        print_r($matches);
    }
     
    /* на выходе:
    Array
    (
        [0] => Array
            (
                [0] => <a href="http://arts-up.ru">Привет</a> <a href="http://arts-up.ru">Мир!</a>
            )
    )
    */


На этот раз регулярное выражение нашло только первый открывающийся и закрывающийся тег. В народе техника приведённая выше называется “Сетка”

Если в регулярное выражение мы добавим (.*?) то эта техника снова заработает и в случае строки без переноса:

    $html = '<a href="http://arts-up.ru">Привет</a> <a href="http://arts-up.ru">Мир!</a>';
     
    // заметьте ?
    if (preg_match_all('/<a.*?>.*?<\/a>/',$html,$matches)) {
        print_r($matches);
    }
     
    /* на выходе:
    Array
    (
        [0] => Array
            (
                [0] => <a href="http://arts-up.ru">Hello</a>
                [1] => <a href="http://arts-up.ru">World!</a>
            )
    )
    */


Теперь всё работает нормально.
Предыдущие и последующие выражения

И снова начнём с примера. Данное регулярное выражение ищет строку foo за которой следует строка bar:

    $pattern = '/foo(?=bar)/';
     
    preg_match($pattern,'Hello foo'); // false
    preg_match($pattern,'Hello foobar'); // true


А теперь наоборот:
    $pattern = '/foo(?!bar)/';
     
    preg_match($pattern,'Hello foo'); // true
    preg_match($pattern,'Hello foobar'); // false
    preg_match($pattern,'Hello foobaz'); // true


Поиск последующего выражения происходит до того, как произошло нахождение предыдущего выражения. Используйте ?< для позитивных выражений, _

Ещё пример:
    $pattern = '/(?<!foo)bar/';
     
    preg_match($pattern,'Hello bar'); // true
    preg_match($pattern,'Hello foobar'); // false
    preg_match($pattern,'Hello bazbar'); // true


Шаблоны для условий (If-Then-Else)

Формат таких выражений выглядит следующим образом:(?(condition)true-pattern|false-pattern) или (?(condition)true-pattern)

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

    $pattern =  '/^(<)?[a-z]+(?(1)>)$/';
     
    preg_match($pattern, ''); // true
    preg_match($pattern, ''); // false
    preg_match($pattern, 'hello'); // true


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

    // начинаем с поиска 'q',
    // если таковых символов нет ищем 'f'
    $pattern = '/^(?(?=q)qu|f)/';
     
    preg_match($pattern, 'quake'); // true
    preg_match($pattern, 'qwerty'); // false
    preg_match($pattern, 'foo'); // true
    preg_match($pattern, 'bar'); // false


Фильтрование

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

В следующем примере мы используем строку, которая содержит символ (*):
    $word = '*мир*';
    $text = 'Привет *мир*!';
     
    preg_match('/'.$word.'/', $text); // вызовет warning
    preg_match('/'.preg_quote($word).'/', $text); // tru
e

Того же самого эффекта может добиться, заключив строку между \Q и \E.
    $word = '*мир*';
    $text = 'Привет *мир*!';
    preg_match('/\Q'.$word.'\E/', $text); // true


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

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

Начнём с небольшого примера:
    preg_match('/(f.*)(b.*)/', 'Hello foobar', $matches);
     
    echo "f* => " . $matches[1]; // prints 'f* => foo'
    echo "b* => " . $matches[2]; // prints 'b* => bar'


Теперь произведём небольшое изменение, добавив (H.*) в начало (часто распространённая ловушка). Нарушилась последовательность индексов:
    preg_match('/(H.*) (f.*)(b.*)/', 'Hello foobar', $matches);
    echo "f* => " . $matches[1]; // prints 'f* => Hello'
    echo "b* => " . $matches[2]; // prints 'b* => foo'


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

    preg_match('/(?:H.*) (f.*)(b.*)/', 'Hello foobar', $matches);
     
    echo "f* => " . $matches[1]; // prints 'f* => foo'
    echo "b* => " . $matches[2]; // prints 'b* => bar'


Добавив ‘?:’, мы сообщаем, что не добавляем этот элемент в массив. Так что последовательность индексов не нарушится.
Именованные подшаблоны

Существует еще один метод для предотвращения ловушек, как в предыдущем примере. Мы можем каждому подшаблону дать своё имя, а потом ссылаться на них. Формат: (? Ppattern)

Перепишем прошлые пример:
    preg_match('/(?Pf.*)(?Pb.*)/', 'Hello foobar', $matches);
    echo "f* => " . $matches['fstar']; // prints 'f* => foo'
    echo "b* => " . $matches['bstar']; // prints 'b* => bar'


И второй:
    preg_match('/(?PH.*) (?Pf.*)(?Pb.*)/', 'Hello foobar', $matches);
    echo "f* => " . $matches['fstar']; // prints 'f* => foo'
    echo "b* => " . $matches['bstar']; // prints 'b* => bar'
    echo "h* => " . $matches['hi']; // prints 'h* => Hello'



Есть много мнений о том, что не следует использовать регулярные выражения для обработки [X]HTML. Однако иногда возникает такая задача:

К слову сказать полезно знать как происходит парсинг XML и HTML. Пример: вытащить вторую ссылку из страницы:
   
$doc = DOMDocument::loadHTML('
        <html>
        <body>Тест
            <a href="http://arts-up.ru">Первая ссылка</a>
            <a href="http://www.arts-up.ru">Вторая ссылка</a>
        </body>
        </html>
    ');
     
    echo $doc->getElementsByTagName('a')
            ->item(1)
            ->getAttribute('href');
     
    // на выходе: http://www.arts-up.ru



Перевел: Станислав Протасевич



Теги: prints, template, выражения, Array, preg_replace_callback(), str_replace(), preg_replace(), регулярные выражения

Раздел: Уроки PHP
Дата: 7-02-2012, 06:35 | Добавил: WorldPad | Читали: 441  |  

Другие новости по теме:

Комментарии:

Оставить комментарий
Имя:*
E-Mail:
Введите код: *

Опрос

Как давно вы программируете на PHP?

только начал
около месяца
больше года
очень давно

 

Поисковая статистика

↓ Апдейты Яndex тИЦ
13.04.2012
16.02.2012
04.02.2012
↓ Апдейты выдачи Яndexa
17.05.2012
15.05.2012
12.05.2012
↓ Апдейты ЯКаталога
18.05.2012
16.05.2012
11.05.2012