Статья
Версия для печати
Обсудить на форуме
Сложности с многобайтными кодировками в PHP.


Автор: Петрелевич Сергей
Версия: 2.0
Дата написания: 16.02.2010
Сайт автора: www.SmartyIT.ru

Публикуется с разрешения автора.
Под редакцией Клуба программистов «Весельчак У».

Часто при обработке текста на русском языке начинающие PHP-программисты удивляются, почему после обрезания строки, она вместо последней буквы может заканчиваться непонятным символом. Часто причиной этого «эффекта» является многобайтная кодировка.

Давайте разберемся, что это такое.
Многобайтной кодировкой символов называют кодировку, в которой один символ представлен несколькими байтами. Типичным примером многобайтной кодировки является UTF-8. UTF-8 набирает все большую популярность в Web-программировании, поэтому рассмотрим именно ее.
Вот что говорит Википедия по поводу многобайтности UTF-8:

Цитата: Википедия
Символы, закодированные в UTF-8, могут быть длиной до шести байт, однако стандарт Unicode не определяет символов выше 0x10ffff, поэтому символы Unicode могут иметь максимальный размер в 4 байта в UTF-8.

Для нас сейчас важно, что один символ в кодировке UTF-8 может определяться несколькими байтами, а не одним байтом.
Поскольку есть многобайтные кодировки, то должны быть и однобайтные. В качестве примера однобайтной кодировки можно взять — ASCII.
Справка из Викпедии:

Цитата: Википедия
ASCII представляет собой 7-битную кодировку для представления десятичных цифр, латинского и национального алфавитов, знаков препинания и управляющих символов. В компьютерах обычно используют 8-битные расширения ASCII.

Т.е. в кодировке ASCII, один символ представлен одним байтом.

Теперь перейдем к практическому примеру.
Допустим, из строки «Тестовая строка» мы хотим вырезать первые четыре символа. Для этого воспользуемся функций PHP substr, пишем:

Код: (PHP)
substr( 'Тестовая строка', 0, 4);

А вот результат, зависит от того в какой кодировке находится строка. Если кодировка ASCII, то результат будет, как и ожидается, «Тест». А в случае UTF-8 результат изменится на «Те». Причина такого отличия заключается в особенности работы функции substr. Substr работает не с символами строки, а с байтами. Поэтому, если строка в однобайтной кодировке, т.е. число байт равно числу символов, то функция отработает корректно. Если же количество байт не равно количеству символов, то результат будет неверным.
В данном примере substr отрезал от строки четыре байта. Символы кириллицы кодируются в UTF-8 двумя байтами, вот и получилось в результате два символа, вместо четырех. Если бы мы отрезали нечетное количество символов, то в результате получили бы «усечение» последнего символа, это бы и вызвало странный визуальный эффект в конце строки.

Для корректной работы с многобайтными кодировками в PHP предусмотрен набор функций, их легко можно узнать по наличию приставки «mb_».
Рассмотрим корректный код усечения строки в многобайтной кодировка:

Код: (PHP)
  mb_internal_encoding("UTF-8");       // указываем нужную кодировку
  mb_substr( 'Тестовая строка', 0, 4); // выполняем преобразование строки

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

Как видите, работа с многобайтной кодировкой в PHP не является сложной задачей, но имеет ряд особенностей, про которые надо помнить: это указание кодировки и использование специальных функций с приставкой «mb_».
Версия для печати
Обсудить на форуме