Персональный сайт
Романа Парпалака

Заметки
 
Блог
 
Программы
 
Фото
ГлавнаяЗаметкиТехнологииВеб-разработка → PHP и UTF-8

PHP и UTF-8

31 марта 2008 года

Если на одной веб-странице требуется использование символов из нескольких языков, то для такой страницы проще всего взять кодировку UTF-8. В связи с этим многие ругают PHP за отсутствие встроенной поддержки этой кодировки. Конечно, PHP может ее поддерживать, но это сделано через зад через расширения вроде mbstring. Признать такой способ удачным и универсальным нельзя.

Проблема состоит в том, что обычные функции для обработки строк, вроде strlen, используют принцип «один байт — один символ». Поэтому, если им передать строку, в которой некоторые символы закодированы несколькими байтами, могут произойти всякие неприятности.

Однако, если посмотреть на способ кодирования символов в UTF-8 и подумать, то станет ясно, что почти во всех ситуациях неприятностей можно избежать. Нужно помнить только, что такие функции, как strlen и substr, принимают на вход и выдают не номера символов, а номера байт. Регулярные выражения, str_replace и другие функции будут работать правильно.

Одна из редких ситуаций, в которых стандартных функций будет недостаточно, возникает тогда, когда от какой-нибудь строки нужно отрезать, например, ровно 10 символов. Скольким байтам в UTF-строке соответствуют эти символы, неизвестно. В таких ситуациях неплохо будет работать следующий код:

<?php if (!function_exists('mb_internal_encoding')) {    function mb_strlen($str)    {       for ($i strlen($str), $j 0$i--; )          if ((ord($str[$i]) & 0xc0) != 0x80)             $j++;          return $j;    }    function mb_substr($str$from$len false)    {       if ($from >= 0)       {          for ($c_byte 0$from--; )             if (ord($str[$c_byte]) <= 0x7F)                $c_byte++;             else                while ((ord($str[++$c_byte]) & 0xc0) == 0x80);          $byte_beg $c_byte;          if ($len === false)             return substr($str$byte_beg);          elseif ($len 0)          {             for ($c_byte strlen($str) - 1$len++; $c_byte--)                if (ord($str[$c_byte]) > 0x7F)                   while ((ord($str[--$c_byte]) & 0xc0) == 0x80);             return substr($str$byte_beg, -strlen($str) + $c_byte);          }          else          {             for ( ; $len--; )                if (ord($str[$c_byte]) <= 0x7F)                   $c_byte++;                else                   while ((ord($str[++$c_byte]) & 0xc0) == 0x80);             return substr($str$byte_beg$c_byte $byte_beg);          }       }       else       {          $last_byte strlen($str) - 1;          for ($c_byte $last_byte$from++; $c_byte--)             if (ord($str[$c_byte]) > 0x7F)                while ((ord($str[--$c_byte]) & 0xc0) == 0x80);          $byte_beg $c_byte;          if ($len === false)             return substr($str$byte_beg $last_byte);          elseif ($len 0)          {             for ($c_byte $last_byte$len++; $c_byte--)                if (ord($str[$c_byte]) > 0x7F)                   while ((ord($str[--$c_byte]) & 0xc0) == 0x80);             return substr($str$byte_beg $last_byte$c_byte $last_byte);          }          else          {             for ( ; $len--; )                if (ord($str[$c_byte]) <= 0x7F)                   $c_byte++;                else                   while ((ord($str[++$c_byte]) & 0xc0) == 0x80);             return substr($str$byte_beg $last_byte$c_byte $byte_beg);          }       }    } } else    mb_internal_encoding('UTF-8'); ?>

Теперь, чтобы взять первые 10 символов строки, достаточно написать mb_substr($str, 0, 10);.

Вот, в принципе, и всё. В следующей версии SiteX'а основной кодировкой будет UTF-8.

Комментарии:

1. 7 августа 2008 года, 14:41. anonIMouS пишет:
С помощью регулярок все эти функции значительно упрощаются. Например,
function mb_strlen($str) {
return strlen(preg_replace('#[\x80-\xBF]#s',",$str));
}
2. 7 августа 2008 года, 21:32. пишет:
Да, вы правы. И это решение работает быстрее, чем использование цикла.

На самом деле, мне сначала понадобилась функция utf8_substr. Вариант с регулярными выражениями для малых входных параметров (именно такие мне были нужны) работал медленнее, чем с циклом. Поэтому в utf8_strlen я оставил цикл.

Я исправлю заметку.
3. 4 ноября 2008 года, 02:52. пишет:
4. 4 ноября 2008 года, 20:33. пишет:
Возможно это и попытка изобрести велосипед.

Чтобы быть более конкретным, скажу, что речь шла об укорачивании слишком длинных ссылок:

http://punbb.informer.com/trac/browser/punbb/tags/punbb-1.3-RC2/include/parser.php#L595

Сейчас там используется другой велосипед:

http://punbb.informer.com/trac/browser/punbb/tags/punbb-1.3-RC2/include/utf8

У меня нет никакого желания выяснять, какой из велосипедов быстрее :)
5. 21 февраля 2010 года, 19:08. zalivnoy пишет:
Вот еще про проблему обработки «русских» строк:
http://www.smartyit.ru/php/56

Оставьте свой комментарий

Ваше имя:


Ваш e-mail:





Комментарий:

Для выделения используйте следующий код: [I]курсив[/I], [B]жирный[/B].
Цитату оформляйте так: [Q = имя автора]цитата[/Q] или [Q]еще цитата[/Q].
Ссылку начните с http://. Других команд или HTML-тегов здесь нет.


Сколько будет 16+2?


Еще в разделе «Веб-разработка»:
На тему «PHP»
Посмотрите в блоге:
PHP
наверх