Является ли тестирование строк UTF-8 в PHP надежным методом? - PullRequest
5 голосов
/ 15 марта 2012

Я нашел полезную функцию в другом ответе, и мне интересно, может ли кто-нибудь объяснить мне, что он делает, и является ли он надежным. Я использовал mb_detect_encoding (), но он был некорректным при чтении из файла ISO 8859-1 в ОС Linux.

Эта функция работает во всех случаях, которые я тестировал.

Вот вопрос: Получить кодировку файла

Вот функция:

function isUTF8($string){
    return preg_match('%(?:
    [\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);
}

Является ли это надежным способом обнаружения строк UTF-8? Что именно он делает? Можно ли сделать его более надежным?

Ответы [ 6 ]

6 голосов
/ 15 марта 2012

Если вы не знаете кодировку строки, невозможно угадать кодировку с какой-либо степенью точности.Вот почему mb_detect_encoding просто не работает.Однако, если вы знаете, в какой кодировке должна быть строка , вы можете проверить, является ли эта строка допустимой в этой кодировке, используя mb_check_encoding.Он более или менее делает то, что делает ваше регулярное выражение, возможно, чуть более всесторонне.Он может ответить на вопрос «Является ли эта последовательность байтов действительной в UTF-8?» с четким указанием «да» или «нет».Это не обязательно означает, что строка на самом деле закодирована в этой кодировке, просто это может быть.Например, будет невозможно отличить какую-либо однобайтовую кодировку, использующую все 8 битов, от любой другой однобайтовой кодировки, использующей 8 битов.Но UTF-8 должен быть достаточно различимым, хотя вы можете создавать, например, строки в кодировке Latin-1, которые также являются действительными последовательностями байтов UTF-8.нет способа узнать наверняка.Если вы ожидаете UTF-8, проверьте правильность полученной вами последовательности байтов в UTF-8, тогда вы можете безопасно обрабатывать строку как UTF-8.Кроме этого, вы вряд ли сможете что-либо сделать.

0 голосов
/ 23 марта 2012

Рассматриваемая функция (та, которую пользователь pilif разместил в связанном вопросе), по-видимому, была взята из этого комментария на странице mb_detect_encoding() в руководстве по PHP.:

Как заявляет автор, функция предназначена только для «проверки, содержит ли строка символов UTF-8» и ищет только «не ascii многобайтовые последовательности в UTF-».8 ассортимент ».Следовательно, функция возвращает false (фактически, ноль), если ваша строка просто содержит простые символы ascii (например, текст на английском языке), что, вероятно, не то, что вам нужно.

Его функция была основана на другой функции в thisпредыдущий комментарий на той же странице, который фактически предназначен для проверки того, является ли строка UTF-8 и основан на это регулярное выражение , созданное кем-то в W3C.

Вот оригинальная, правильно работающая (я проверял) функция, которая сообщит вам, является ли строка UTF-8:

// Returns true if $string is valid UTF-8 and false otherwise.
function is_utf8($string) {

    // From http://w3.org/International/questions/qa-forms-utf-8.html
    return 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);

} // function is_utf8
0 голосов
/ 15 марта 2012

В основном, нет.

  • Любая Строка UTF8 является допустимой 8-битной строкой кодирования (даже если она генерирует бред).
  • С другой стороны, большинство 8-битовых кодированных строк с расширенными (128+) символами являются не допустимым UTF8, но, как и любая другая случайная последовательность байтов, они может случиться.
  • И, конечно же, любой текст ASCII является действительным UTF8, поэтому, на самом деле, mb_detect_encoding правильно сказать. И нет, у вас не будет проблем с использованием текста ASCII в качестве UTF8. Именно поэтому UTF8 работает в первую очередь.

Насколько я понимаю, функция, которую вы предоставили, не проверяет достоверность строки, просто она содержит некоторые последовательности, которые оказываются похожими на последовательности в UTF8, поэтому эта функция может пропускать много хуже. Вы можете использовать обе эти функции и mb_detect_encoding в строгом режиме и надеяться, что они отменяют ложные срабатывания друг друга.

Если текст написан на нелатинском алфавите, «умным» способом обнаружения многобайтового кодирования является поиск последовательностей одинаковых по размеру кусков байтов, начинающихся с одинаковых битов. Например, русское слово "привет" выглядит так:

11010000 10111111
11010001 10000000
11010000 10111000
11010000 10110010
11010000 10110101
11010001 10000010

Это, однако, не будет работать для латинских алфавитов (и, возможно, для китайского).

0 голосов
/ 15 марта 2012

Возможно, это не ответ на ваш вопрос (может быть, так оно и есть, см. Обновление ниже), но это может быть ответом на вашу проблему.Проверьте мой класс кодирования, в котором есть методы для преобразования строк в UTF8, независимо от того, кодируются ли они уже в Latin1, Win1252 или UTF8, или их комбинация:

Encoding::toUTF8($text_or_array);
Encoding::toWin1252($text_or_array);
Encoding::toISO8859($text_or_array);

// fixes UTF8 strings converted to UTF8 repeatedly: 
//  "FÃÂédÃÂération" to "Fédération"
Encoding::fixUTF8($text_or_array);  

https://stackoverflow.com/a/3479832/290221

Функция работает побайтово и выясняет, требуется ли преобразование для каждого из них.

Обновление:

Подумав немного об этом, на самом деле это может бытьответ на ваш вопрос:

require_once('Encoding.php');

function validUTF8($string){
    return Encoding::toUTF8($string) == $string;
}

А вот класс кодировки: https://github.com/neitanod/forceutf8

0 голосов
/ 15 марта 2012

Это будет просто определять, является ли часть строки формально допустимой последовательностью UTF-8, игнорируя закодированные символы одной кодовой единицы (представляющие кодовые точки в ASCII).Для того, чтобы эта функция вернула true, достаточно, чтобы был один символ, который выглядит как не кодированный ASCII символ UTF-8.

0 голосов
/ 15 марта 2012

Ну, он только проверяет, есть ли в строке байтовые последовательности, которые соответствуют действительным кодовым точкам UTF-8.Однако он не помечает последовательность 0x00-0x7F, которая является ASCII-совместимым подмножеством UTF-8.

РЕДАКТИРОВАТЬ: Между прочим, я предполагаю, что причина, по которой mb_detect_encoding() «не работает должным образом», была изВаш файл в кодировке Latin-1 использовал только ASCII-совместимое подмножество, которое также действует в UTF-8.Неудивительно, что mb_detect_encoding() помечает это как UTF-8, и это «правильно», если данные просто ASCII, тогда ответ UTF-8 так же хорош, как Latin-1, или ASCII, или любой из множестварасширенные кодировки ASCII.

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