Я использую 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; } }
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 документ
Вы, конечно же, хотели бы установить для него необязательный параметр «strict» в TRUE. Но я не уверен, что это действительно охватывает все недопустимые последовательности UTF-8. Функция не претендует на проверку байтовой последовательности для действительности UTF-8. Известны случаи, когда mb_detect_encoding раньше догадывался UTF-8, хотя я не знаю, может ли это произойти в строгом режиме.
Если вы хотите быть уверенным, сделайте это самостоятельно, используя рекомендованное W3 regex :
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);
С библиотекой mbstring у вас есть mb_check_encoding () .
Пример использования:
mb_check_encoding($string, 'UTF-8');
Когда производительность имеет значение, это быстрее, чем регулярное выражение, предоставленное в принятом ответе.
Быстрый тест в моей конфигурации показывает (для 20 000 итераций):
РЕДАКТИРОВАТЬ
С помощью PHP 7.1.9 в последней системе Windows 10 решение regex превосходит mb_check_encoding()
для любой длины строки (все еще 20 000 итераций):
mb_check_encoding()
, mb_check_encoding()
=> 64ms mb_check_encoding()
=> 2.4s Просто заметьте: вместо использования часто рекомендуемого (довольно сложного) регулярного выражения W3C вы можете просто использовать модификатор 'u' для проверки строки для UTF-8:
<?php if (preg_match("//u", $string)) { // $string is valid UTF-8 }
Посмотрите http://www.phpwact.org/php/i18n/charsets для руководства по кодировкам. Эта страница ссылается на страницу специально для utf8.
ответ на «iconv is idempotent»
ни один 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)
Не уверен, достигнет ли это того же, но не мог бы вы просто использовать utf8_encode()
для всего текста, не беспокоясь об обнаружении? Если текст уже UTF-8, это не повредит. И если это не так, оно будет преобразовано. Если вы уже подумали об этом, есть ли причина, по которой это не сработает?