Поиск подстроки в строке PHP: strpos, stripos, preg_match | IT-tower

📑 Содержание

Введение

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

strpos и stripos — позиция первого вхождения

Функция strpos() ищет первое вхождение подстроки и возвращает её позицию (число, начиная с 0) или false, если подстрока не найдена.

💡 Доступность: PHP 4, PHP 5, PHP 7, PHP 8
strpos — базовый пример
$string = "Привет, мир! Как дела?";

// Поиск "мир" в строке
$pos = strpos($string, "мир");
var_dump($pos); // int(9)

// Подстрока не найдена
$pos = strpos($string, "вселенная");
var_dump($pos); // bool(false)

Критическая ошибка при использовании strpos

Позиция 0 — это валидный результат (подстрока найдена в начале строки), но 0 == false в PHP. Поэтому проверку нужно делать строго через ===:

Правильная проверка
// НЕПРАВИЛЬНО
if (strpos($string, "Привет")) {
    echo "Найдено";
}
// Не сработает, если подстрока в начале строки (позиция 0)

// ПРАВИЛЬНО
if (strpos($string, "Привет") !== false) {
    echo "Найдено";
}

Поиск с указанной позиции

Третий параметр $offset позволяет начать поиск с определенной позиции.

💡 Доступность параметра $offset: PHP 5+
strpos с offset
$string = "один два три два один";

// Начать поиск с позиции 5 (пропустить первое "два")
$pos = strpos($string, "два", 5);
var_dump($pos); // int(14) — второе вхождение

stripos — поиск без учета регистра

Функция stripos() работает идентично strpos, но игнорирует регистр символов.

💡 Доступность: PHP 5+
stripos
$string = "Привет, Мир!";

$pos = strpos($string, "мир");  // false — регистр важен
$pos = stripos($string, "мир"); // int(9) — регистр игнорируется

strrpos и strripos — позиция последнего вхождения

Функция strrpos() ищет последнее вхождение подстроки и возвращает его позицию.

💡 Доступность strrpos: PHP 4, PHP 5, PHP 7, PHP 8
💡 Доступность strripos: PHP 5+
strrpos
$string = "один два три два один";

$pos = strrpos($string, "два");
var_dump($pos); // int(14) — последнее вхождение

Эти функции полезны, когда нужно найти последнее расширение файла или последний разделитель:

Практический пример
$filename = "document.backup.tar.gz";
$pos = strrpos($filename, ".");
$extension = substr($filename, $pos + 1); // "gz"

str_contains — простая проверка наличия подстроки

До PHP 8.0 для проверки наличия подстроки использовали конструкцию strpos() !== false. В PHP 8.0 появилась функция str_contains(), которая возвращает true или false.

💡 Доступность: PHP 8.0+
str_contains
$string = "PHP — это язык программирования";

var_dump(str_contains($string, "язык"));      // bool(true)
var_dump(str_contains($string, "Python"));    // bool(false)
var_dump(str_contains($string, ""));          // bool(true) — пустая строка всегда содержится

Когда использовать str_contains, а когда strpos

Используйте str_contains, если вам нужно только проверить наличие подстроки. Используйте strpos, если вам нужна позиция вхождения.

Выбор функции
// Только проверка — str_contains (PHP 8.0+)
if (str_contains($email, "@")) {
    echo "Корректный email";
}

// Нужна позиция — strpos (PHP 4+)
$pos = strpos($email, "@");
$domain = substr($email, $pos + 1);

strstr, stristr, strchr, strrchr — извлечение подстроки

Эти функции не просто находят подстроку, а возвращают часть строки начиная с найденного вхождения.

strstr и strchr

strstr() возвращает часть строки от первого вхождения и до конца. strchr() — это псевдоним (alias) функции strstr().

💡 Доступность strstr: PHP 4, PHP 5, PHP 7, PHP 8
💡 Доступность strchr: PHP 4+ (псевдоним)
strstr
$email = "user@example.com";

$domain = strstr($email, "@");
var_dump($domain); // string(12) "@example.com"

Параметр $before_needle

Четвертый параметр $before_needle = true возвращает часть строки ДО вхождения.

💡 Доступность параметра: PHP 5.3+
strstr с before_needle
$email = "user@example.com";

$user = strstr($email, "@", true);
var_dump($user); // string(4) "user"

stristr — без учета регистра

stristr() работает как strstr, но игнорирует регистр.

💡 Доступность: PHP 4, PHP 5, PHP 7, PHP 8
stristr
$string = "Hello World";
$result = stristr($string, "WORLD");
var_dump($result); // string(5) "World"

strrchr — от последнего вхождения

strrchr() возвращает часть строки от последнего вхождения символа.

💡 Доступность: PHP 4, PHP 5, PHP 7, PHP 8
strrchr
$path = "/var/www/html/index.php";
$filename = strrchr($path, "/");
var_dump($filename); // string(10) "/index.php"
⚠️ Внимание: strrchr ищет одиночный символ, а не подстроку. Если передать строку длиннее одного символа, используется только первый символ.

substr_count — подсчет количества вхождений

Функция substr_count() подсчитывает, сколько раз подстрока встречается в строке.

💡 Доступность: PHP 4, PHP 5, PHP 7, PHP 8
substr_count
$text = "Мама мыла раму. Мама любит раму.";

$count = substr_count($text, "Мама");
var_dump($count); // int(2)

Параметры $offset и $length

Можно указать смещение и длину области поиска.

💡 Доступность параметров: PHP 5.1+
substr_count с offset и length
$text = "Мама мыла раму. Мама любит раму.";

// Искать только в первых 20 символах
$count = substr_count($text, "раму", 0, 20);
var_dump($count); // int(1)

Важная особенность

substr_count не учитывает пересекающиеся вхождения:

Пересекающиеся вхождения
$string = "aaa";
$count = substr_count($string, "aa");
var_dump($count); // int(1), а не 2

Мультибайтовые версии (mb_*)

Все вышеперечисленные функции работают с однобайтовыми кодировками (ASCII, Latin-1). Для UTF-8, кириллицы и других многобайтовых кодировок нужно использовать функции с префиксом mb_:

Однобайтовая Мультибайтовая Описание Доступность mb_*
strpos() mb_strpos() Позиция первого вхождения PHP 4.0.6+
stripos() mb_stripos() Позиция без учета регистра PHP 5.2+
strrpos() mb_strrpos() Позиция последнего вхождения PHP 4.0.6+
strripos() mb_strripos() Последнее без учета регистра PHP 5.2+
strstr() mb_strstr() Часть от первого вхождения PHP 4.0.6+
stristr() mb_stristr() То же без учета регистра PHP 4.0.6+
strrchr() mb_strrchr() Часть от последнего вхождения PHP 4.0.6+
substr_count() mb_substr_count() Подсчет вхождений PHP 4.3+

Почему это важно

Разница между strpos и mb_strpos
$string = "Привет мир";

// Обычная strpos считает байты, а не символы
$pos = strpos($string, "мир");
var_dump($pos); // int(13) — неверно для UTF-8

// mb_strpos считает символы
$pos = mb_strpos($string, "мир");
var_dump($pos); // int(7) — верно

Указание кодировки

Мультибайтовые функции принимают параметр кодировки:

Указание кодировки
$pos = mb_strpos($string, "мир", 0, "UTF-8");

Начиная с PHP 8.0, кодировку можно задать глобально через mb_internal_encoding(), и параметр можно опускать.

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

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

preg_match — поиск первого совпадения

💡 Доступность: PHP 4, PHP 5, PHP 7, PHP 8
preg_match
$text = "Заказ №12345 оформлен 01.01.2024";

// Найти все цифры
if (preg_match("/\d+/", $text, $matches)) {
    var_dump($matches[0]); // string(5) "12345"
}

// Найти дату в формате ДД.ММ.ГГГГ
if (preg_match("/(\d{2})\.(\d{2})\.(\d{4})/", $text, $matches)) {
    var_dump($matches[0]); // string(10) "01.01.2024"
    var_dump($matches[1]); // string(2) "01" — день
    var_dump($matches[2]); // string(2) "01" — месяц
    var_dump($matches[3]); // string(4) "2024" — год
}

preg_match_all — поиск всех совпадений

💡 Доступность: PHP 4, PHP 5, PHP 7, PHP 8
preg_match_all
$text = "Яблоки: 5, Груши: 3, Апельсины: 8";

preg_match_all("/(\d+)/", $text, $matches);
var_dump($matches[0]); // array(3) { [0]=>"5", [1]=>"3", [2]=>"8" }

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

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

💡 Доступность preg_quote: PHP 4, PHP 5, PHP 7, PHP 8
preg_quote для точного поиска
$needle = "цена: 100 руб.";
$pattern = "/" . preg_quote($needle, "/") . "/";

if (preg_match($pattern, $text)) {
    echo "Найдено";
}

Сравнительная таблица: что когда использовать

Задача Функция Мин. версия PHP Пример
Проверить наличие подстроки str_contains() PHP 8.0 str_contains($s, "abc")
Проверить наличие (PHP 7 и ниже) strpos() !== false PHP 4 strpos($s, "abc") !== false
Позиция первого вхождения strpos() PHP 4 strpos($s, "abc")
Позиция без учета регистра stripos() PHP 5 stripos($s, "abc")
Позиция последнего вхождения strrpos() PHP 4 strrpos($s, "abc")
Извлечь часть от вхождения strstr() PHP 4 strstr($email, "@")
Извлечь часть до вхождения strstr($s, $n, true) PHP 5.3 strstr($email, "@", true)
Извлечь без учета регистра stristr() PHP 4 stristr($s, "abc")
Извлечь от последнего вхождения strrchr() PHP 4 strrchr($path, "/")
Подсчитать вхождения substr_count() PHP 4 substr_count($s, "abc")
Подсчитать с ограничением области substr_count($s, $n, $o, $l) PHP 5.1 substr_count($s, "a", 0, 10)
Поиск по шаблону preg_match() PHP 4 preg_match("/\d+/", $s)
Найти все совпадения preg_match_all() PHP 4 preg_match_all("/\d+/", $s)
Работа с UTF-8 (позиция) mb_strpos() PHP 4.0.6 mb_strpos($s, "мир")
Работа с UTF-8 (без регистра) mb_stripos() PHP 5.2 mb_stripos($s, "мир")
Работа с UTF-8 (последняя позиция) mb_strrpos() PHP 4.0.6 mb_strrpos($s, "мир")
Работа с UTF-8 (подсчет) mb_substr_count() PHP 4.3 mb_substr_count($s, "мир")

Типичные ошибки и нюансы

Ошибка 1: Нестрогое сравнение с false

Нестрогое сравнение
// НЕПРАВИЛЬНО
if (strpos($string, $needle)) { ... }

// ПРАВИЛЬНО
if (strpos($string, $needle) !== false) { ... }

// ЕЩЕ ЛУЧШЕ (PHP 8.0+)
if (str_contains($string, $needle)) { ... }

Ошибка 2: Использование однобайтовых функций с UTF-8

UTF-8 и strlen
$string = "Привет";

// strlen вернет 12 (6 символов × 2 байта)
echo strlen($string); // 12

// mb_strlen вернет 6
echo mb_strlen($string); // 6

Ошибка 3: Поиск пустой строки

Пустая строка
// strpos всегда вернет 0 для пустой строки
var_dump(strpos("текст", "")); // int(0)

// str_contains всегда вернет true (PHP 8.0+)
var_dump(str_contains("текст", "")); // bool(true)

Ошибка 4: Регистрозависимость по умолчанию

Регистрозависимость
$string = "PHP";

var_dump(str_contains($string, "php")); // false (PHP 8.0+)
var_dump(stripos($string, "php") !== false); // true

Для регистронезависимой проверки в PHP 8+ нет прямой функции str_icontains, поэтому используйте stripos:

Кастомная str_icontains
function str_icontains(string $haystack, string $needle): bool {
    return stripos($haystack, $needle) !== false;
}

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

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

preg_match стоит использовать только тогда, когда нужен поиск по шаблону. Для поиска точной подстроки через регулярки не забудьте про preg_quote.

Относительная скорость (от быстрого к медленному)

  1. str_contains / strpos — самые быстрые
  2. mb_strpos — чуть медленнее из-за обработки многобайтовых символов
  3. preg_match — самый медленный, но самый гибкий

Заключение

В PHP есть полный набор инструментов для поиска подстроки в строке. Выбор конкретной функции зависит от задачи:

  • Нужно просто проверить наличие? Используйте str_contains (PHP 8.0+) или strpos !== false (PHP 4+).
  • Нужна позиция? Используйте strpos (PHP 4+) или strrpos (PHP 4+).
  • Нужно извлечь часть строки? Используйте strstr (PHP 4+) или strrchr (PHP 4+).
  • Работаете с UTF-8? Используйте мультибайтовые версии mb_* (PHP 4.0.6+).
  • Нужен поиск по шаблону? Используйте preg_match (PHP 4+) или preg_match_all (PHP 4+).
✅ Итог: Знание всех этих функций позволяет выбирать оптимальный инструмент для каждой конкретной задачи и писать более читаемый и производительный код.

Готовы обсудить ваш проект?

Оставьте заявку, и я свяжусь с вами в течение 2 часов в рабочее время (Пн-Пт 9:00-18:00).