Обеспечение действительного utf-8 в PHP - PullRequest
19 голосов
/ 06 октября 2009

Я использую 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;
    }
}

Ответы [ 6 ]

37 голосов
/ 06 октября 2009

UTF-8 может хранить любой символ Unicode. Если ваша кодировка - это что-то еще, включая 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

Вы, безусловно, захотите установить для этого необязательного параметра ’strict’ ​​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);
14 голосов
/ 21 ноября 2011

С библиотекой mbstring у вас есть mb_check_encoding () .

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

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

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

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

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

EDIT

В PHP 7.1.9 в последней системе Windows 10 решение regex превосходит mb_check_encoding() при любой длине строки (до 20 000 итераций):

  • 10 символов: регулярное выражение => 4 мс, mb_check_encoding() => 64 мс
  • 10000 символов: регулярное выражение => 125 мс, mb_check_encoding() => 2,4 с
3 голосов
/ 11 июня 2013

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

<?php
  if (preg_match("//u", $string)) {
      // $string is valid UTF-8
  }
1 голос
/ 06 октября 2009

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

0 голосов
/ 26 февраля 2014

ответ на "iconv isimpmpotent"

также не iconv - iconv не идемпотентный

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

iconv ('ISO-8859-1', 'UTF-8'. '// IGNORE', $ str)

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

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

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

0 голосов
/ 06 октября 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...