Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save codedokode/ff99e357e9860ea169b8 to your computer and use it in GitHub Desktop.
Save codedokode/ff99e357e9860ea169b8 to your computer and use it in GitHub Desktop.

Revisions

  1. codedokode revised this gist Jun 22, 2018. 1 changed file with 4 additions and 0 deletions.
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,7 @@
    Статья переехала в мой гитхаб: https://github.com/codedokode/pasta/blob/master/php/strings-utf8.md . Ниже идет устаревшая и неточная версия.

    ---

    Некоторые функции PHP (`strlen`, `substr`, а также обращение к строке как к массиву: `$str[0]`) не работают с многобайтовыми кодировками (вроде utf-8). В utf-8 1 символ закодирован с помощью от 1 до 6 байтов, а эти функции думают, что 1 буква всегда кодируется одним байтом. По этой причине они ломают символы, в результате получаются битые символы и ничего не работает. Потому вместо них надо использовать mb_ функции например `mb_strlen`, `mb_substr`. Вместо доступа к строке как к массиву надо использовать `mb_substr`.

    Если тебе интересно, почему эти функции поддерживают только однобайтные кодировки, а не многобайтные, то причина в том, что они очень старые и написаны в то время (лет 40 назад) когда utf-8 и многобайтных кодировок еще не было.
  2. codedokode revised this gist Dec 20, 2014. 1 changed file with 2 additions and 0 deletions.
    Original file line number Diff line number Diff line change
    @@ -16,6 +16,8 @@

    Латинница и цифры кодируются в utf-8 одним байтом, с ними это работает, но все равно, не надо использовать эти функции — это слишком ненадежно и легко сделать ошибку.

    Также, чтобы работать с русскими (и другими нелатинскими) буквами в регулярках, надо ставить в конце флаг `u`: `preg_match("/[абвг]/u", $string)`. Иначе `preg_match` будет думать что работает с однобайтной кодировкой и будет видеть не одну букву, а 2 latin1-символа (так как русская буква кодируется как 2 байта). Например, буква `л` кодирующаяся как `208 187` будет восприниматься как 2 символа с кодами `208` и `187`, то есть `л` (кодировка latin-1: http://en.wikipedia.org/wiki/ISO/IEC_8859-1#Codepage_layout ). Таким образом, регулярка будет работать некорректно и найдет не то.

    Вывод: используй `mb_*` функции. Не используй доступ к строке как к массиву. В регулярных выражениях используй флаг `u` (он говорит что используется utf-8 а не однобайтовая кодировка).

    Некоторые строковые функции без префикса mb тем не менее корректно работают с utf-8 и их можно использовать. Вот они: `strtr` (если передавать массив), `str_replace`, `str_repeat`, `explode`, `addslashes`, `trim`.
  3. codedokode revised this gist Nov 16, 2014. 1 changed file with 1 addition and 1 deletion.
    Original file line number Diff line number Diff line change
    @@ -20,7 +20,7 @@

    Некоторые строковые функции без префикса mb тем не менее корректно работают с utf-8 и их можно использовать. Вот они: `strtr` (если передавать массив), `str_replace`, `str_repeat`, `explode`, `addslashes`, `trim`.

    **Не** работают с utf-8: `strrev`, `strlen`, `substr`, `strpos`, `ucfirst`, `wordwrap` и большинство других строковых функций.
    **Не** работают с utf-8: `strrev`, `strlen`, `substr`, `strpos`, `ucfirst`, `wordwrap`, `str_pad` и большинство других строковых функций, для работы которых нужно считать число символов. Не работает задание ширины в функциях вроде `sprintf` и `printf`.

    ## mbstring.func_overload

  4. codedokode revised this gist Nov 16, 2014. 1 changed file with 1 addition and 1 deletion.
    Original file line number Diff line number Diff line change
    @@ -16,7 +16,7 @@

    Латинница и цифры кодируются в utf-8 одним байтом, с ними это работает, но все равно, не надо использовать эти функции — это слишком ненадежно и легко сделать ошибку.

    Вывод: используй `mb_*` функции. Не используй доступ к строке как к массиву. В регулярных выражениях используй флаг `u` (он говорит что исплоьзуется utf-8 а не однобайтовая кодировка).
    Вывод: используй `mb_*` функции. Не используй доступ к строке как к массиву. В регулярных выражениях используй флаг `u` (он говорит что используется utf-8 а не однобайтовая кодировка).

    Некоторые строковые функции без префикса mb тем не менее корректно работают с utf-8 и их можно использовать. Вот они: `strtr` (если передавать массив), `str_replace`, `str_repeat`, `explode`, `addslashes`, `trim`.

  5. codedokode revised this gist Nov 16, 2014. 1 changed file with 1 addition and 1 deletion.
    Original file line number Diff line number Diff line change
    @@ -16,7 +16,7 @@

    Латинница и цифры кодируются в utf-8 одним байтом, с ними это работает, но все равно, не надо использовать эти функции — это слишком ненадежно и легко сделать ошибку.

    Вывод: используй `mb_*` функции. Не исплоьзуй доступ к строке как к массиву. В регулярных выражениях используй флаг `u` (он говорит что исплоьзуется utf-8 а не однобайтовая кодировка).
    Вывод: используй `mb_*` функции. Не используй доступ к строке как к массиву. В регулярных выражениях используй флаг `u` (он говорит что исплоьзуется utf-8 а не однобайтовая кодировка).

    Некоторые строковые функции без префикса mb тем не менее корректно работают с utf-8 и их можно использовать. Вот они: `strtr` (если передавать массив), `str_replace`, `str_repeat`, `explode`, `addslashes`, `trim`.

  6. codedokode revised this gist Nov 16, 2014. 1 changed file with 1 addition and 1 deletion.
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    Некоторые функции PHP (`strlen`, `substr`, а также обращение к строке как к массиву: `$str[0]`) не работают с многобайтовыми кодировками (вроде utf-8). В utf-8 1 символ закодирован с помощью от 1 до 6 байтов, а эти функции думают, что 1 буква вегда кодируется одним байтом. По этой причине они ломают символы, в результате получаются битые символы и ничего не работает. Потому вместо них надо использовать mb_ функции например `mb_strlen`, `mb_substr`. Вместо доступа к строке как к массиву надо использовать `mb_substr`.
    Некоторые функции PHP (`strlen`, `substr`, а также обращение к строке как к массиву: `$str[0]`) не работают с многобайтовыми кодировками (вроде utf-8). В utf-8 1 символ закодирован с помощью от 1 до 6 байтов, а эти функции думают, что 1 буква всегда кодируется одним байтом. По этой причине они ломают символы, в результате получаются битые символы и ничего не работает. Потому вместо них надо использовать mb_ функции например `mb_strlen`, `mb_substr`. Вместо доступа к строке как к массиву надо использовать `mb_substr`.

    Если тебе интересно, почему эти функции поддерживают только однобайтные кодировки, а не многобайтные, то причина в том, что они очень старые и написаны в то время (лет 40 назад) когда utf-8 и многобайтных кодировок еще не было.

  7. codedokode revised this gist Oct 10, 2014. 1 changed file with 2 additions and 0 deletions.
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,7 @@
    Некоторые функции PHP (`strlen`, `substr`, а также обращение к строке как к массиву: `$str[0]`) не работают с многобайтовыми кодировками (вроде utf-8). В utf-8 1 символ закодирован с помощью от 1 до 6 байтов, а эти функции думают, что 1 буква вегда кодируется одним байтом. По этой причине они ломают символы, в результате получаются битые символы и ничего не работает. Потому вместо них надо использовать mb_ функции например `mb_strlen`, `mb_substr`. Вместо доступа к строке как к массиву надо использовать `mb_substr`.

    Если тебе интересно, почему эти функции поддерживают только однобайтные кодировки, а не многобайтные, то причина в том, что они очень старые и написаны в то время (лет 40 назад) когда utf-8 и многобайтных кодировок еще не было.

    Давай разберем пример. Допустим, у нас есть строка из русской буквы «щ» в кодирове utf-8. Попытаемся взять первую букву с помощью неправильной функции:

    // Внимание! это неправильный код, не пиши так!
  8. codedokode revised this gist Oct 10, 2014. 1 changed file with 1 addition and 1 deletion.
    Original file line number Diff line number Diff line change
    @@ -22,4 +22,4 @@

    ## mbstring.func_overload

    В неоторых (неграмотных) учебниках ты можешь увидеть совет включить опцию mbstring.fun_overload (подробнее про нее: http://php.net/manual/ru/mbstring.overload.php ). Ни в коем случае так не делай, так как это изначально неправильно спроектированная опция. Она не решает проблему, для которой задумывалась (включить в старом приложении использующем функции вроде `strlen` поддержку utf-8), а лишь создает путаницу. Например, при ее включении `strlen` заменяется на поддерживающую utf-8 `mb_strlen`, но `ucfirst` ни на что не заменяется и не работает.
    В неоторых (неграмотных) учебниках ты можешь увидеть совет включить опцию `mbstring.func_overload` (подробнее про нее: http://php.net/manual/ru/mbstring.overload.php ). Ни в коем случае так не делай, так как это изначально неправильно спроектированная опция. Она не решает проблему, для которой задумывалась (включить в старом приложении использующем функции вроде `strlen` поддержку utf-8), а лишь создает путаницу. Например, при ее включении `strlen` заменяется на поддерживающую utf-8 `mb_strlen`, но `ucfirst` ни на что не заменяется и не работает.
  9. codedokode revised this gist Oct 10, 2014. 1 changed file with 4 additions and 0 deletions.
    Original file line number Diff line number Diff line change
    @@ -19,3 +19,7 @@
    Некоторые строковые функции без префикса mb тем не менее корректно работают с utf-8 и их можно использовать. Вот они: `strtr` (если передавать массив), `str_replace`, `str_repeat`, `explode`, `addslashes`, `trim`.

    **Не** работают с utf-8: `strrev`, `strlen`, `substr`, `strpos`, `ucfirst`, `wordwrap` и большинство других строковых функций.

    ## mbstring.func_overload

    В неоторых (неграмотных) учебниках ты можешь увидеть совет включить опцию mbstring.fun_overload (подробнее про нее: http://php.net/manual/ru/mbstring.overload.php ). Ни в коем случае так не делай, так как это изначально неправильно спроектированная опция. Она не решает проблему, для которой задумывалась (включить в старом приложении использующем функции вроде `strlen` поддержку utf-8), а лишь создает путаницу. Например, при ее включении `strlen` заменяется на поддерживающую utf-8 `mb_strlen`, но `ucfirst` ни на что не заменяется и не работает.
  10. codedokode created this gist Oct 8, 2014.
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,21 @@
    Некоторые функции PHP (`strlen`, `substr`, а также обращение к строке как к массиву: `$str[0]`) не работают с многобайтовыми кодировками (вроде utf-8). В utf-8 1 символ закодирован с помощью от 1 до 6 байтов, а эти функции думают, что 1 буква вегда кодируется одним байтом. По этой причине они ломают символы, в результате получаются битые символы и ничего не работает. Потому вместо них надо использовать mb_ функции например `mb_strlen`, `mb_substr`. Вместо доступа к строке как к массиву надо использовать `mb_substr`.

    Давай разберем пример. Допустим, у нас есть строка из русской буквы «щ» в кодирове utf-8. Попытаемся взять первую букву с помощью неправильной функции:

    // Внимание! это неправильный код, не пиши так!
    $s = "щ";
    $x = substr($s, 0, 1);

    Буква «щ» кодируется в utf-8 как 2 байта: `209 137` (я взял информацию тут: http://www.utf8-chartable.de/unicode-utf8-table.pl?start=1024&utf8=dec ). substr отрезает от строки не первую букву, а первый **байт**. Это значит, что в $x он положит 1 байт с кодом `209`. В utf-8 это неверная последовательность, она не соответвует никакому символу (так как после `209` обязательно должно идти второе число). Ideone может вообще отказаться что-то отображать, встретив такой код.

    То же самое, когда ты обращаешься к строке как к массиву: `$s[0]`. Эта команда берет не первую букву, а только первый байт строки. Естественно, такая программа не будет работать.

    Функция `strlen` считает число байт (не букв) в строке. То есть в данном случае `strlen($s)` вернет нам 2.

    Латинница и цифры кодируются в utf-8 одним байтом, с ними это работает, но все равно, не надо использовать эти функции — это слишком ненадежно и легко сделать ошибку.

    Вывод: используй `mb_*` функции. Не исплоьзуй доступ к строке как к массиву. В регулярных выражениях используй флаг `u` (он говорит что исплоьзуется utf-8 а не однобайтовая кодировка).

    Некоторые строковые функции без префикса mb тем не менее корректно работают с utf-8 и их можно использовать. Вот они: `strtr` (если передавать массив), `str_replace`, `str_repeat`, `explode`, `addslashes`, `trim`.

    **Не** работают с utf-8: `strrev`, `strlen`, `substr`, `strpos`, `ucfirst`, `wordwrap` и большинство других строковых функций.