Обеспечение допустимого utf-8 в PHP


Я использую PHP для обработки текста из различных источников. Я не ожидаю, что это будет что-то иное, кроме UTF-8, ISO-8859-1 или, возможно, WINDOWS-1252. Если это что-то другое, кроме одного из них, мне просто нужно убедиться, что текст будет преобразован в допустимую строку UTF-8, даже если символы потеряны. Решает ли это опция //TRANSLIT в iconv? Например, будет ли этот код гарантировать, что строка безопасна для вставки в документ (или базу данных) в кодировке UTF-8?

function make_safe_for_utf8_use($string) {

    $encoding = mb_detect_encoding($string, "UTF-8,ISO-8859-1,WINDOWS-1252");

    if ($encoding != 'UTF-8') {
        return iconv($encoding, 'UTF-8//TRANSLIT', $string);
    } else {
        return $string;
    }
}
Author: Benjamin, 2009-10-06

6 answers

UTF-8 может хранить любой символ Юникода. Если ваша кодировка вообще какая-либо другая, включая ISO-8859-1 или Windows-1252, UTF-8 может хранить в ней каждый символ. Таким образом, вам не нужно беспокоиться о потере каких-либо символов при преобразовании строки из любой другой кодировки в UTF-8.

Кроме того, как ISO-8859-1, так и Windows-1252 являются однобайтовыми кодировками, в которых допустим любой байт. Технически невозможно провести различие между ними. Я бы выбрал Windows-1252 в качестве соответствия по умолчанию для последовательностей, отличных от UTF-8, поскольку единственными байтами, которые декодируются по-разному, являются диапазон 0x80-0x9F. Они декодируются в различные символы, такие как смарт-кавычки и евро в Windows-1252, тогда как в ISO-8859-1 они являются невидимыми управляющими символами, которые почти никогда не используются. Веб-браузеры иногда могут говорить, что они используют ISO-8859-1, но часто они действительно будут использовать Windows-1252.

Будет ли этот код гарантировать, что строка безопасна для вставки в документ с кодировкой UTF-8

Ты для этой цели, безусловно, хотелось бы установить необязательный параметр "строгий" в значение TRUE. Но я не уверен, что это действительно охватывает все недопустимые последовательности UTF-8. Функция не утверждает, что явно проверяет последовательность байтов на правильность UTF-8. Были известны случаи, когда mb_detect_encoding раньше неправильно угадывал UTF-8, хотя я не знаю, может ли это все еще происходить в строгом режиме.

Если вы хотите быть уверены, сделайте это самостоятельно, используя Регулярное выражение, рекомендованное W3:

if (preg_match('%^(?:
      [\x09\x0A\x0D\x20-\x7E]            # ASCII
    | [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
    | \xE0[\xA0-\xBF][\x80-\xBF]         # excluding overlongs
    | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
    | \xED[\x80-\x9F][\x80-\xBF]         # excluding surrogates
    | \xF0[\x90-\xBF][\x80-\xBF]{2}      # planes 1-3
    | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
    | \xF4[\x80-\x8F][\x80-\xBF]{2}      # plane 16
)*$%xs', $string))
    return $string;
else
    return iconv('CP1252', 'UTF-8', $string);
 33
Author: bobince, 2011-12-11 11:54:35

С библиотекой mbstring у вас есть mb_check_ кодирование().

Пример использования:

mb_check_encoding($string, 'UTF-8');

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

Быстрый тест моей конфигурации показывает (для 20 000 итераций):

  • регулярное выражение: ~310 мс
  • mb_check_encoding: ~90 мс

РЕДАКТИРОВАТЬ

С PHP 7.1.9 в последней версии Windows 10 система, решение регулярного выражения превосходит mb_check_encoding() для любой длины строки (все еще 20 000 итераций):

  • 10 символов: регулярное выражение =>4 мс, mb_check_encoding() =>64 мс
  • 10000 символов: регулярное выражение =>125 мс, mb_check_encoding() =>2,4 с
 12
Author: Frosty Z, 2017-10-05 09:57:06

Просто примечание: Вместо того, чтобы использовать часто рекомендуемое (довольно сложное) регулярное выражение от W3C, вы можете просто использовать модификатор 'u' для проверки строки на правильность UTF-8:

<?php
  if (preg_match("//u", $string)) {
      // $string is valid UTF-8
  }
 3
Author: eyecatchUp, 2013-06-11 10:45:46

Взгляните на http://www.phpwact.org/php/i18n/charsets для руководства по кодировкам. Эта страница ссылается на страницу, специально предназначенную для utf8.

 1
Author: Martijn, 2009-10-06 06:19:29

Ответ на "iconv является идемпотентным"

Также не является iconv - iconv не является идемпотентным

Большая разница между utf8_encode() и iconv() является ли то, что iconv может вызывать ошибки, подобные этой "Обнаружен неполный многобайтовый символ во входной строке" даже с

Iconv('ISO-8859-1', 'UTF-8'.'//ИГНОРИРОВАТЬ', $str)

В приведенном выше коде:

$кодирование = mb_detect_encoding($строка, "UTF-8, ISO-8859-1, WINDOWS-1252");

Вы должны знайте, что mb_detect_encoding может ответить на uft-8 даже для недопустимых строк utf-8 (неправильно сформированных utf8)

 0
Author: Nadir, 2014-02-26 14:01:03

Не уверен, что это приведет к тому же, но не могли бы вы просто использовать utf8_encode() для всего текста, не беспокоясь об обнаружении? Если текст уже содержит UTF-8, это не повредит ему. А если это не так, то оно будет преобразовано. Если вы уже думали об этом, есть ли причина, по которой это не сработает для вас?

 -1
Author: Marc W, 2009-10-06 03:40:09