Освоение регулярных выражений с помощью preg_match

Раздел: Работа со строками -> Обработка строк

Функция preg_match в PHP: базовые возможности

Основное эффективное решение

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


$subject = 'Привет, мир! PHP 8.3';
$pattern = '/[0-9]+\.[0-9]+/';
if (preg_match($pattern, $subject, $matches)) {
    echo 'Версия: ' . $matches[0]; // 8.3
} else {
    echo 'Совпадение не найдено';
}

Php только цифры (оставить только цифры в php)

Здесь шаблон ищет числа с точкой (версия PHP). $matches[0] содержит всю найденную подстроку. Если совпадение не найдено, код выполняет ветку else.

Типичные ошибки

  • Забывают проверить возвращаемое значение на false - при синтаксической ошибке в шаблоне preg_match возвращает false, а не 0. Без проверки это может быть воспринято как отсутствие совпадения.
  • Не экранируют обратные слеши в шаблоне, использующем специальные символы (например, \d). В двойных кавычках PHP обратный слеш может интерпретироваться, поэтому рекомендуется использовать одинарные кавычки или дополнительное экранирование.
  • Путают preg_match с preg_match_all - первая возвращает только первое совпадение, вторая - все.

Как проверить, содержит ли строка определённый шаблон?

Простая проверка без извлечения данных: достаточно передать только $subject и $pattern, а аргумент $matches можно опустить.


if (preg_match('/^[A-Z]/', 'Hello')) {
    echo 'Строка начинается с заглавной буквы';
}

подстрока строки php (поиск подстроки в строке php)

Проблема

Если не указать модификатор u для Юникода, кириллические заглавные буквы не будут распознаны.


// Неверно для кириллицы:
preg_match('/^[А-Я]/u', 'Привет'); // корректно с u

Php строки в файле (строки в файле php)

Как извлечь первую найденную подстроку с захватом групп?

Функция позволяет захватывать части совпадения с помощью круглых скобок. Результат помещается в массив $matches.


$string = 'Цена: 1500 руб.';
preg_match('/(\d+)\s*руб/', $string, $m);
echo $m[0]; // 1500 руб.
echo $m[1]; // 1500

Php preg match (функция preg_match в php)

Нулевой элемент - полное совпадение, первый элемент - первая захваченная группа.

Типичная ошибка

При указании несуществующего индекса массива возникает предупреждение. Всегда проверяйте наличие совпадений (if (preg_match(...))).

Как получить позицию найденного совпадения?

Флаг PREG_OFFSET_CAPTURE заставляет $matches содержать не только строку, но и её позицию (в байтах или символах в зависимости от модификатора).


$text = 'Код 123, цена 456';
preg_match('/\d+/', $text, $m, PREG_OFFSET_CAPTURE);
print_r($m[0]); // Array ( [0] => 123 [1] => 4 )

убрать теги php (удаление html-тегов в php)

$m[0][0] - найденное значение, $m[0][1] - позиция в строке (начиная с 0).

Проблема

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

Как использовать именованные группы для удобства?

Именованные группы задаются синтаксисом (?P<name>...) или (?<name>...) (с PHP 7.2). Доступ к ним - по имени ключа.


$date = '2025-03-21';
preg_match('/(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})/', $date, $m);
echo $m['year']; // 2025
echo $m['month']; // 03

Php убрать из строки (удаление подстроки в php)

Типичная ошибка

Путаница с синтаксисом: ?P<name> - регистр имеет значение. Кроме того, имена групп должны быть уникальными в шаблоне.

Как обрабатывать кириллицу и Unicode?

Модификатор u включает Юникод-режим. Без него шаблон работает с байтами, и кириллица может не совпадать.


$text = 'Москва - столица';
preg_match('/[а-я]+/u', $text, $m);
echo $m[0]; // Москва (без u вернуло бы пусто или байты)

Проблема

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

Как отличить ошибку регулярного выражения от отсутствия совпадения?

При ошибке (например, неверный шаблон) preg_match возвращает false. Для детализации используется preg_last_error().


$result = @preg_match('/[a-z/', 'test'); // ошибка: незакрытый класс
if ($result === false) {
    $error = preg_last_error();
    if ($error === PREG_INTERNAL_ERROR) {
        echo 'Внутренняя ошибка';
    } elseif ($error === PREG_BACKTRACK_LIMIT_ERROR) {
        echo 'Превышен лимит backtrack';
    } else {
        echo 'Код ошибки: ' . $error;
    }
}

Типичная ошибка

Использование оператора @ для подавления ошибок - временное решение. Лучше проверять шаблон заранее или обрабатывать исключение.

Как выполнить поиск без учёта регистра?

Модификатор i - игнорирование регистра.


$email = 'User@Example.COM';
if (preg_match('/^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/i', $email)) {
    echo 'Email валиден';
}

Проблема

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

Расширенные примеры использования preg_match

Ниже приведены нетривиальные сценарии, демонстрирующие продвинутые возможности функции.

Извлечение данных с помощью PREG_OFFSET_CAPTURE для нескольких групп

Пример

$log = 'Ошибка в 10:23:45 на строке 42';
$pattern = '/(\d{2}):(\d{2}):(\d{2})/';
preg_match($pattern, $log, $m, PREG_OFFSET_CAPTURE);

echo 'Время: ' . $m[0][0] . ' (позиция ' . $m[0][1] . ')' . "\n";
echo 'Часы: ' . $m[1][0] . ' (поз. ' . $m[1][1] . ')' . "\n";
echo 'Минуты: ' . $m[2][0] . ' (поз. ' . $m[2][1] . ')' . "\n";
echo 'Секунды: ' . $m[3][0] . ' (поз. ' . $m[3][1] . ')' . "\n";
Время: 10:23:45 (позиция 8)
Часы: 10 (поз. 8)
Минуты: 23 (поз. 11)
Секунды: 45 (поз. 14)

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

Именованные группы с модификатором u для кириллицы

Пример

$message = 'Сотрудник: Иванов Иван, отдел: Продажи';
$pattern = '/Сотрудник:\s*(?P<name>[А-Яа-я\s]+),\s*отдел:\s*(?P<department>[А-Яа-я]+)/u';
preg_match($pattern, $message, $m);
echo 'Имя: ' . $m['name'] . "\n";
echo 'Отдел: ' . $m['department'] . "\n";
Имя: Иванов Иван
Отдел: Продажи

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

Применение PREG_UNMATCHED_AS_NULL для корректной обработки необязательных групп

Флаг PREG_UNMATCHED_AS_NULL (PHP 7.2+) делает незахваченные группы равными null вместо пустой строки. Это удобно при работе с необязательными частями шаблона.

Пример

$url = 'https://example.com/path?q=1';
$pattern = '/(https?:)?\/\/([^\/]+)(\/[^?]+)?(\?.*)?/';
preg_match($pattern, $url, $m, PREG_UNMATCHED_AS_NULL);
var_dump($m[3]); // string(5) "/path"
var_dump($m[4]); // string(5) "?q=1"

$url2 = 'http://site.com';
preg_match($pattern, $url2, $m2, PREG_UNMATCHED_AS_NULL);
var_dump($m2[3]); // NULL
var_dump($m2[4]); // NULL
string(5) "/path"
string(5) "?q=1"
NULL
NULL

Без этого флага $m[3] было бы пустой строкой "", что могло бы ввести в заблуждение.

Рекурсивные шаблоны для парсинга вложенных структур (например, скобок)

Пример

$code = 'f(g(x), h(y(z)))';
$pattern = '/\(([^()]|(?R))*\)/';
preg_match($pattern, $code, $m);
echo $m[0]; // (g(x), h(y(z)))
(g(x), h(y(z)))

Здесь (?R) - рекурсия всего шаблона. Это позволяет захватывать вложенные скобки. Однако такой шаблон может быть медленным и требовать больших лимитов backtrack.

Использование PREG_OFFSET_CAPTURE совместно с именованными группами

Пример

$html = '
Контент
'; $pattern = '//'; preg_match($pattern, $html, $m, PREG_OFFSET_CAPTURE); echo 'ID элемента: ' . $m['id'][0] . ' на позиции ' . $m['id'][1];
ID элемента: main на позиции 9

Комбинация модификаторов s, m, x

Пример

$multi = "Строка 1\nСтрока 2";
// Режим многострочности m: ^ и $ работают для каждой строки
$pattern = '/^Строка\s+\d$/m';
preg_match($pattern, $multi, $m);
echo $m[0]; // Строка 2

// Режим s: точка включает перенос строки
$pattern2 = '/Строка.+2/s';
preg_match($pattern2, $multi, $m2);
echo $m2[0]; // Строка 1\nСтрока 2

// Режим x: игнорирование пробелов в шаблоне (для читаемости)
$pattern3 = '/^Строка \s+ \d $/mx';
preg_match($pattern3, $multi, $m3);
echo $m3[0]; // Строка 2
Строка 2
Строка 1
Строка 2
Строка 2
Строка 1
Строка 2
Строка 2

Модификаторы можно комбинировать: imsu - регистронезависимый, многострочный, точка включает перенос, Юникод.

Обработка больших строк и лимиты backtrack

Пример

$bigString = str_repeat('a', 100000) . 'b';
$pattern = '/(a+)+b/';
$result = preg_match($pattern, $bigString);
if ($result === false) {
    $err = preg_last_error();
    if ($err === PREG_BACKTRACK_LIMIT_ERROR) {
        echo 'Превышен лимит backtrack. Увеличьте pcre.backtrack_limit в php.ini.';
    }
}
Превышен лимит backtrack. Увеличьте pcre.backtrack_limit в php.ini.

Шаблоны с вложенными квантификаторами (как (a+)+) могут вызвать катастрофический backtrack. Решение - упростить шаблон или увеличить лимит.

Сравнение preg_match с preg_match_all для извлечения всех совпадений

Пример

$csv = 'item1, item2, item3';
// только первое
preg_match('/item\d+/', $csv, $m);
echo 'preg_match: ' . $m[0] . "\n";
// все
preg_match_all('/item\d+/', $csv, $all);
print_r($all[0]);
preg_match: item1
Array
(
    [0] => item1
    [1] => item2
    [2] => item3
)

Для получения всех совпадений используйте preg_match_all.

Функция preg_match в PHP - comments

En
Php preg match (php)