Байты и символы: mb_*
До сих пор все строки в примерах были латиницей. Давайте посчитаем длину русского слова:
<?php
echo strlen('cat'), ' ', strlen('кот'), "\n"; // 3 6
var_dump('кот'); // string(6) "кот"В обоих словах по три буквы, а strlen насчитала в русском шесть. var_dump согласна с ней: как вы уже знаете, число в скобках после string — длина строки.
Строка — это байты
Вернёмся к ряду плиток. Плитка в строке PHP — это байт, самая маленькая ячейка памяти: в ней помещается одно число от 0 до 255.
Латинская буква, цифра или пробел занимают одну плитку. Кириллической букве одной плитки мало, и она занимает две.
Правило, по которому буквы раскладываются по байтам, называют кодировкой. Почти все файлы и сайты используют кодировку UTF-8: в ней латиница занимает один байт, а кириллица — два.
strlen, substr и str_pad считают плитки, а не буквы. Для латиницы это одно и то же, для кириллицы — нет. Для работы с буквами в PHP есть функции с приставкой mb_: они знают, что буква может занимать несколько байт.
Функции mb_* даёт расширение mbstring. Laravel Herd ставит его вместе с PHP, а в Linux его ставят отдельным пакетом. Если расширения нет, вызов mb_strlen останавливает программу с ошибкой Call to undefined function mb_strlen().
Давайте пройдём по шагам программу со словом привет. Нажимайте «Следующий шаг» и сравнивайте $bytes и $chars, а потом $upper и $mbUpper:
1<?php2 3$word = 'привет';4$bytes = strlen($word);5$chars = mb_strlen($word);6$upper = strtoupper($word);7$mbUpper = mb_strtoupper($word);8echo $bytes, ' ', $chars, "\n";9echo $upper, ' ', $mbUpper, "\n";
В слове шесть букв, и каждая кириллическая буква занимает два байта.
Правило, которое показывает виджет:
strlen,substr,str_pad,strtoupperи другие обычные функции работают с байтами;mb_strlen,mb_substr,mb_str_pad,mb_strtoupperиmb_strtolowerработают с буквами;- для латиницы обе группы дают одно и то же, для кириллицы — нет.
На собеседовании часто спрашивают, почему strlen для русского текста возвращает число вдвое больше, чем в нём букв. Ответ: строка в PHP — это байты, а кириллическая буква в UTF-8 занимает два байта.
Регистр букв
С PHP 8.2 функции strtoupper, strtolower и ucfirst меняют только латинские буквы от a до z. Русское слово они возвращают как есть, и для него берут mb_strtoupper и mb_strtolower. Функция mb_ucfirst, которая делает заглавной первую букву, появилась в PHP 8.4.
Кусок и длина по буквам
Обычные функции режут и дополняют строку по байтам, а функции mb_ — по буквам:
$word = 'привет';
echo substr($word, 0, 4), "\n"; // пр
echo mb_substr($word, 0, 4), "\n"; // прив
echo str_pad('кот', 5, '*'), "\n"; // кот
echo mb_str_pad('кот', 5, '*'), "\n"; // кот**Разберём по строкам:
substr($word, 0, 4)берёт четыре байта — это две буквы.mb_substr($word, 0, 4)берёт четыре буквы.str_pad('кот', 5, '*')ничего не добавляет: в слове уже шесть байт, больше пяти.mb_str_padдополняет слово до пяти букв. Эта функция появилась в PHP 8.3.
Если взять нечётное число байт, кусок оборвётся посередине буквы, и на экране вместо неё появится непонятный значок. По той же причине $word[0] возвращает не букву п, а только её первый байт.
Здесь плитки перестают быть точной картинкой. Кириллическая буква занимает две плитки, только если файл сохранён в кодировке UTF-8. Редакторы кода сохраняют так по умолчанию, но в файле со старой кодировкой Windows-1251 каждая буква занимает один байт, и strlen('кот') даёт 3.
Частая ошибка
Длину имени проверяют через strlen:
$name = 'Александра';
if (strlen($name) > 10) { // ✗
echo 'name too long', "\n";
}Программа печатает name too long, хотя в имени ровно 10 букв: strlen насчитала 20 байт. PHP ничего не сообщает, и ошибку замечают, когда русские имена не проходят проверку, а английские такой же длины проходят. Исправление — mb_strlen($name) > 10.
Главное
- Строка в PHP — ряд байт; в UTF-8 латинская буква занимает один байт, а кириллическая — два.
strlen('привет')— это12, аmb_strlen('привет')—6.strlen,substr,str_padиstrtoupperработают с байтами, а их версии с приставкойmb_— с буквами.- Для длины, кусков и регистра русского текста берут функции
mb_*.