Посимвольный вывод слов

306
17 августа 2017, 23:37

Здравствуйте!

Есть такая конструкция, которая выведет первую букву переменной $str, которая будет равна 't':

mb_internal_encoding("UTF-8");
$str = 'test';
print_r($str[0]);   //    выведет 't'

Но почему это не работает с русскими словами?

mb_internal_encoding("UTF-8");
$str = 'тест';
print_r($str[0]);   //    выведет '�', вместо 'т'

Кодировка вроде выставлена, но почему в выводе знак вопроса?

Подскажите, пожалуйста, как решить?

Спасибо!

Answer 1

Всё дело в том, что в кодировке UTF-8 единичный символ может занимать разное количество байт (от 1 до 6 или даже больше).

Английские буквы, цифры, некоторые знаки препинания занимают по одному байту, и их представление ничем не отличается, как если бы они были закодированы в ASCII. Для букв русского алфавита, букв европейских языков, которых нет английском (например, немецкая Ö), для арабского алфавита, нужно уже по два байта. Распространённые восточные иероглифы занимают уже по 3 байта каждый. А всеми любимые эмодзи кодируются последовательностями от 4 байтов.

Итак, давайте рассмотрим байтовое представление ваших строк.

  • test -> 74 65 73 74
  • тест -> D1 82 D0 B5 D1 81 D1 82

Слева показано то, как эти буквы видим мы, справа — их побайтовое представление в памяти компьютера, причём значения байтов записаны в 16-ричной системе счисления (чтобы не занять много места). Как видите, русское слово занимает в два раза больше памяти, хотя букв в нём столько же. Кроме того, обратите внимание, каждый байт в его кодированном варианте не меньше 8016.

Теперь вернёмся к PHP. Строки в этом языке о кодировках ничего не знают, а просто хранят в себе только байты. И оператор индексирования тоже ничего про кодировки, буквы, символы не знает; он просто, будучи применённым к строке, возвращает байт с указанным номером. Поэтому, когда вы пытаетесь напечатать первый байт (с номером 0), в первом случае выводится байт со значением 7416, который на экране компьютера виден как маленькая латинская буква «t». А во втором случае на вывод подаётся байт со значением D116, представляющий собой огрызок русской буквы «т»; не зная, какой байт идёт дальше, устройство вывода (терминал или веб-обозреватель) просто рисует знак вопроса.

Что же делать, как напечатать только первый символ из строки? В качестве решения можно применить функции из расширения php-mbstring.

$letter = mb_substr($str, 0, 1);  // вернёт первый code point из строки

Но учтите, функция mb_substr оперирует понятием code point, а не graphical cluster. Какое же это имеет значение? Стандарт юникода позволяет составлять некоторые буквы из нескольких code points. К примеру, буква «ё» может оказаться комбинацией буквы «е» и умляута.

Answer 2

Используйте mb_substr:

mb_substr($string, $letter, 1, "utf-8");
//$string - строка
//$letter - индекс нужной буквы
//1 - сколько символов включая нужную нужно вернуть
//"utf-8" - кодировка
READ ALSO
Как прочитать файл xlsx библиотекой PHPExcel

Как прочитать файл xlsx библиотекой PHPExcel

Здравствуйте, помогите пожалуйста решить вопрос

281
Как запустить Laravel из внешнего php скрипта?

Как запустить Laravel из внешнего php скрипта?

Было написано консольное приложения на Laravel 54 для определённой задачи

264
Как заменить тег h3 на span в виджете?

Как заменить тег h3 на span в виджете?

Подскажите, как в виджете заменить тег h3 на тег span?

356
CKEditor и KCFinder не работают

CKEditor и KCFinder не работают

Проблема с загрузкой картинок на сервер с использованием редактора CKEditor и файлового менеджера KCFinderНа локальном сервере все работает прекрасно,...

280