Работа с фрагментами строк в PHP: от базовых функций до продвинутых техник
Основные методы извлечения подстроки
Какая функция является базовой для получения подстроки?
substr() – стандартная функция PHP для извлечения части строки. Принимает три параметра: исходную строку, начальную позицию (с нуля) и длину (опционально). Если длина не указана, возвращается все до конца строки.
$str = 'Привет, мир!';
echo substr($str, 0, 6); // "Привет"
echo substr($str, 8); // "мир!"
Отрицательные значения start отсчитывают от конца строки. Отрицательная length также работает.
echo substr($str, -4, 2); // "ми"
Типичные ошибки:
- Если start больше длины строки, substr возвращает пустую строку (до PHP 8.0 – false в некоторых случаях).
- При работе с многобайтовыми кодировками (UTF-8) substr оперирует байтами, а не символами. Для кириллицы это приводит к некорректному результату.
Как правильно извлечь подстроку из UTF-8 строки?
Используйте mb_substr() – аналог substr с поддержкой кодировок. Необходимо указать кодировку третьим параметром (по умолчанию UTF-8, если настроена директива mb_internal_encoding).
$str = 'Привет, мир!';
echo mb_substr($str, 0, 6, 'UTF-8'); // "Привет"
echo mb_substr($str, 8, null, 'UTF-8'); // "мир!"
Без указания кодировки или при неверной кодировке mb_substr также может давать сбои. Убедитесь, что расширение mbstring включено.
Как получить один символ из строки по индексу?
Строку можно рассматривать как массив символов (байтов). Обращение по квадратным скобкам $str[$index] возвращает один символ (байт). Для UTF-8 это опасно.
$str = 'Hello';
echo $str[0]; // "H"
echo $str[-1]; // "o" (PHP 7.1+)
$str2 = 'Привет';
echo $str2[0]; // выведет первый байт, не "П"
Ошибка: попытка доступа к несуществующему индексу (больше длины) вызовет warning и вернет пустую строку. Для многобайтовых строк используйте mb_substr($str, 0, 1).
Как извлечь часть строки после определенного символа или слова?
Функция strstr() (или strchr) находит первое вхождение подстроки и возвращает все после неё (включая саму подстроку, если третий параметр true – вернет до неё).
$email = 'user@example.com';
echo strstr($email, '@'); // "@example.com"
echo strstr($email, '@', true); // "user"
// Альтернатива: strpos + substr
$pos = strpos($email, '@');
echo substr($email, $pos + 1); // "example.com"
Если подстрока не найдена, strstr возвращает false. Это может привести к ошибке при выводе. Проверяйте с помощью строгого сравнения.
Как извлечь фрагмент между двумя разделителями?
Комбинация explode(), array_slice() или использование регулярных выражений.
$str = 'начало|фрагмент|конец';
$parts = explode('|', $str);
echo $parts[1]; // "фрагмент"
// Если нужно несколько элементов:
$middle = array_slice($parts, 1, -1);
print_r($middle); // Array([0] => фрагмент)
Проблемы: explode создает массив, что может быть неэффективно для больших строк; регулярные выражения (preg_match) могут быть избыточны, но гибче.
Расширенные примеры работы с подстроками
Ниже приведены подробные примеры, демонстрирующие различные сценарии извлечения частей строк и комбинирование функций.
Извлечение домена из email с конца
Используем strrpos() для поиска последней точки и substr().
$email = 'user@mail.example.com';
$lastDot = strrpos($email, '.');
$domain = substr($email, $lastDot + 1);
echo $domain; // "com"
// Если нужно извлечь домен верхнего уровня вместе с предшествующей частью:
$domainParts = explode('.', $email);
$tld = end($domainParts);
$sld = prev($domainParts);
echo "$sld.$tld"; // "example.com"
com example.com
Обрезка строки до заданной длины с многоточием
Для сокращения длинного текста (например, для превью). Учитываем многобайтовость.
function truncate($text, $maxLength = 100, $ellipsis = '...') {
if (mb_strlen($text) <= $maxLength) {
return $text;
}
return mb_substr($text, 0, $maxLength - mb_strlen($ellipsis)) . $ellipsis;
}
$longText = 'Это очень длинная строка, содержащая много символов, включая русские буквы и эмодзи ?';
echo truncate($longText, 20);
// Результат: "Это очень длинная..."
Это очень длинная...
Извлечение имени файла из пути без расширения
Комбинируем basename() и strrpos().
$path = '/var/www/html/index.html';
$base = basename($path); // "index.html"
$dotPos = strrpos($base, '.');
if ($dotPos !== false) {
$filename = substr($base, 0, $dotPos);
} else {
$filename = $base;
}
echo $filename; // "index"
index
Извлечение подстроки с помощью регулярного выражения
Функция preg_match() позволяет извлечь часть строки по сложному шаблону.
$str = 'Заказ №12345 от 01.02.2023';
$pattern = '/№(\d+)/';
if (preg_match($pattern, $str, $matches)) {
echo $matches[1]; // "12345"
}
// Извлечение даты
$datePattern = '/(\d{2}\.\d{2}\.\d{4})/';
preg_match($datePattern, $str, $dateMatches);
echo $dateMatches[1]; // "01.02.2023"
12345 01.02.2023
Замена части строки с сохранением остального (substr_replace)
substr_replace() заменяет фрагмент строки на другой, не изменяя длину (кроме случая, когда новый фрагмент другой длины).
$str = 'Привет, мир!';
$newStr = substr_replace($str, 'PHP', 8, 3); // заменим "мир" на "PHP"
echo $newStr; // "Привет, PHP!"
// Удаление части: пустая строка
$removed = substr_replace($str, '', 0, 7); // удалим "Привет,"
echo $removed; // " мир!"
Привет, PHP! мир!
Работа с отрицательными индексами в substr
Отрицательный start отсчитывает символы с конца, отрицательная length также возможна (но ведет себя неочевидно).
$str = '123456789';
echo substr($str, -4); // "6789"
echo substr($str, -4, 2); // "67"
echo substr($str, 2, -3); // "3456" – длина от 2 до конца минус 3 символа
6789 67 3456
Сравнение производительности substr и mb_substr для латиницы
Если строка гарантированно однобайтовая (латиница, цифры), substr быстрее.
$start = microtime(true);
for ($i = 0; $i < 100000; $i++) {
substr('Hello World', 0, 5);
}
echo 'substr: ' . (microtime(true) - $start) . "\n";
$start = microtime(true);
for ($i = 0; $i < 100000; $i++) {
mb_substr('Hello World', 0, 5, 'UTF-8');
}
echo 'mb_substr: ' . (microtime(true) - $start);
substr: 0.0045 mb_substr: 0.0121
Обработка эмодзи и суррогатных пар
Эмодзи занимают 2 или 4 байта, но mb_substr корректно обрабатывает их при указании UTF-8.
$emojiStr = 'Привет ? мир!';
echo mb_substr($emojiStr, 7, 1, 'UTF-8'); // "?"
echo substr($emojiStr, 7, 2); // скорее всего сломает эмодзи
? ?? или непонятные байты
Примечание:
Для работы с многобайтовыми строками всегда используйте функции mb_*, а также проверяйте, что кодировка внутреннего представления соответствует ожидаемой (обычно UTF-8).