проверить кодировку текста php

Определение кодировки текста в PHP — обзор существующих решений плюс еще один велосипед

Столкнулся с задачей — автоопределение кодировки страницы/текста/чего угодно. Задача не нова, и велосипедов понапридумано уже много. В статье небольшой обзор найденного в сети — плюс предложение своего, как мне кажется, достойного решения.

Если кратко — он не работает.

Давайте смотреть:

Как видим, на выходе — полная каша. Что мы делаем, когда непонятно почему так себя ведет функция? Правильно, гуглим. Нашел замечательный ответ.

Чтобы окончательно развеять все надежды на использование mb_detect_encoding(), надо залезть в исходники расширения mbstring. Итак, закатали рукава, поехали:

Постить полный текст метода не буду, чтобы не засорять статью лишними исходниками. Кому это интересно посмотрят сами. Нас истересует строка под номером 593, где собственно и происходит проверка того, подходит ли символ под кодировку:

Вот основные фильтры для однобайтовой кириллицы:

Windows-1251 (оригинальные комментарии сохранены)

ISO-8859-5 (тут вообще все весело)

Как видим, ISO-8859-5 всегда возвращает TRUE (чтобы вернуть FALSE, нужно выставить filter->flag = 1).

Когда посмотрели фильтры, все встало на свои места. CP1251 от KOI8-R не отличить никак. ISO-8859-5 вообще если есть в списке кодировок — будет всегда детектиться как верная.

В общем, fail. Оно и понятно — только по кодам символов нельзя в общем случае узнать кодировку, так как эти коды пересекаются в разных кодировках.

2. Что выдает гугл

А гугл выдает всякие убожества. Даже не буду постить сюда исходники, сами посмотрите, если захотите (уберите пробел после http://, не знаю я как показать текст не ссылкой):

http:// deer.org.ua/2009/10/06/1/
http:// php.su/forum/topic.php?forum=1&topic=1346

3. Поиск по хабру

2) на мой взгляд, очень интересное решение: habrahabr.ru/blogs/php/27378/#comment_1399654
Минусы и плюсы в комменте по ссылке. Лично я считаю, что только для детекта кодировки это решение избыточно — слишком мощно получается. Определение кодировки в нем — как побочный эффект ).

4. Собственно, мое решение

Идея возникла во время просмотра второй ссылки из прошлого раздела. Идея следующая: берем большой русский текст, замеряем частоты разных букв, по этим частотам детектим кодировку. Забегая вперед, сразу скажу — будут проблемы с большими и маленькими буквами. Поэтому выкладываю примеры частот букв (назовем это — «спектр») как с учетом регистра, так и без (во втором случае к маленькой букве добавлял еще большую с такой же частотой, а большие все удалял). В этих «спектрах» вырезаны все буквы, имеющие частоты меньше 0,001 и пробел. Вот, что у меня получилось после обработки «Войны и Мира»:

Спектры в разных кодировках (ключи массива — коды соответствующих символов в соответствующей кодировке):

Далее. Берем текст неизвестной кодировки, для каждой проверяемой кодировки находим частоту текущего символа и прибавляем к «рейтингу» этой кодировки. Кодировка с бОльшим рейтингом и есть, скорее всего, кодировка текста.

Результаты

У-упс! Полная каша. А потому что большие буквы в CP1251 обычно соответствуют маленьким в KOI8-R. А маленькие буквы используются в свою очередь намного чаще, чем большие. Вот и определяем строку капсом в CP1251 как KOI8-R.
Пробуем делать без учета регистра («спектры» case insensitive)

Как видим, верная кодировка стабильно лидирует и с регистрозависимыми «спектрами» (если строка содержит небольшое количество заглавных букв), и с регистронезависимыми. Во втором случае, с регистронезависимыми, лидирует не так уверенно, конечно, но вполне стабильно даже на маленьких строках. Можно поиграться еще с весами букв — сделать их нелинейными относительно частоты, например.

5. Заключение

В топике не расмотрена работа с UTF-8 — тут никакий принципиальной разницы нету, разве что получение кодов символов и разбиение строки на символы будет несколько длиннее/сложнее.
Эти идеи можно распространить не только на кириллические кодировки, конечно — вопрос только в «спектрах» соответствующих языков/кодировок.

P.S. Если будет очень нужно/интересно — потом выложу второй частью полностью работающую библиотеку на GitHub. Хотя я считаю, что данных в посте вполне достаточно для быстрого написания такой библиотеки и самому под свои нужды — «спектр» для русского языка выложен, его можно без труда перенести на все нужные кодировки.

UPDATED
В комментариях проскочила замечательная функция, ссылку на которую я опубликовал под графом «убожество». Может быть погорячился со словами, но уж как опубликовал, так опубликовал — редактировать такие вещи не привык. Чтобы не быть голословным, давайте разберемся, работает ли она на 100%, как об этом говорит предполагаемый автор.
1) будут ли ошибки при «нормальной» работе этой функции? Предположим, что контент у нас на 100% валидный.
ответ: да, будут.
2) определит ли она что-нибудь кроме UTF-8 и не-UTF-8?
ответ: нет, не определит.

Источник

Определение кодировки текста в PHP вместо mb_detect_encoding

Существует несколько кодировок символов кириллицы.

При создании сайтов в Интернете обычно используют:

Иногда появляется необходимость определить кодировку текста. И в PHP даже функция для этого есть:

Я протестировал функцию определения кодировки по кодам символов, результат меня удовлетворил и я использовал эту функцию пару лет.

Недавно решил переписать проект где использовал эту функцию, нашел готовый пакет на packagist.org cnpait/detect_encoding, в котором кодировка определяется методом m00t

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

Мне бы установить этот пакет и успокоиться, но я решил «заморочиться».

В общем, сделал свой пакет: onnov/detect-encoding.

Как его использовать написано в README.md

А о его тестировании и сравнении с пакетом cnpait/detect_encoding напишу.

Методика тестирования

Берем большой текст: Tolstoy — Anna Karenina
Всего — 1’701’480 знаков

Убираем все лишнее, оставляем только кириллицу:

Осталось 1’336’252 кирилистических знаков.

В цикле берем часть текста (5, 15, 30,… символов) преобразуем в известную кодировку и пытаемся определить кодировку скриптом. Затем сравниваем правильно или нет.

Вот таблица в которой слева кодировки, сверху количество символов по которому определяем кодировку, в таблице результат достоверности в %%

К счастью, кодировки mac-cyrillic и ibm866 не используются для кодирования веб-страниц.

Точность определения высока даже в коротких предложениях от 5 до 10 букв. А для фраз из 60 букв точность определения достигает 100%. А еще, определение кодировки выполняется очень быстро, например, текст длиной более 1 300 000 символов кириллицы проверяется за 0.00096 секунд. (на моем компьютере)

А какие результаты покажет статистический способ описанный m00t:

Как видим результаты определения кодировки хорошие. Скорость работы скрипта высокая, особенно на коротких текстах, на огромных текстах скорость значительно уступает. Текст длиной более 1 300 000 символов кириллицы проверяется за 0.32 секунд. (на моем компьютере).

Источник

mb_detect_encoding

(PHP 4 >= 4.0.6, PHP 5, PHP 7, PHP 8)

mb_detect_encoding — Определение кодировки символов

Описание

Определяет наиболее вероятную кодировку символов для строки ( string ) string из упорядоченного списка кандидатов.

Автоматическое определение предполагаемой кодировки символов не может быть полностью надёжным; без дополнительной информации это похоже на расшифровку зашифрованной строки без ключа. Всегда предпочтительно использовать индикацию кодировки символов, хранящуюся или передаваемую с данными, такую как HTTP-заголовок «Content-Type».

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

Список параметров

Проверяемая строка ( string ).

Упорядоченный список кодировок символов. Список может быть указан как массив строк или как строка кодировок, разделённых запятыми.

Значение по умолчанию для strict можно установить с помощью параметра конфигурации mbstring.strict_detection.

Возвращаемые значения

Примеры

Пример #1 Пример использования mb_detect_encoding()

Пример #2 Действие параметра strict

// ‘áéóú’ закодирована в ISO-8859-1
$str = «\xE1\xE9\xF3\xFA» ;

Результат выполнения данного примера:

В некоторых случаях одна и та же последовательность байтов может образовывать допустимую строку в нескольких кодировках символов, и невозможно узнать, какая интерпретация предназначалась. Например, среди многих других байтовая последовательность «\xC4\xA2» может быть:

Пример #3 Использование порядка при совпадении нескольких кодировок

Результат выполнения данного примера:

Смотрите также

User Contributed Notes 23 notes

If you try to use mb_detect_encoding to detect whether a string is valid UTF-8, use the strict mode, it is pretty worthless otherwise.

If you need to distinguish between UTF-8 and ISO-8859-1 encoding, list UTF-8 first in your encoding_list:
mb_detect_encoding($string, ‘UTF-8, ISO-8859-1’);

if you list ISO-8859-1 first, mb_detect_encoding() will always return ISO-8859-1.

I used Chris’s function «detectUTF8» to detect the need from conversion from utf8 to 8859-1, which works fine. I did have a problem with the following iconv-conversion.

The problem is that the iconv-conversion to 8859-1 (with //TRANSLIT) replaces the euro-sign with EUR, although it is common practice that \x80 is used as the euro-sign in the 8859-1 charset.

I could not use 8859-15 since that mangled some other characters, so I added 2 str_replace’s:

if(detectUTF8($str)) <
$str=str_replace(«\xE2\x82\xAC»,»€»,$str);
$str=iconv(«UTF-8″,»ISO-8859-1//TRANSLIT»,$str);
$str=str_replace(«€»,»\x80″,$str);
>

If html-output is needed the last line is not necessary (and even unwanted).

Based upon that snippet below using preg_match() I needed something faster and less specific. That function works and is brilliant but it scans the entire strings and checks that it conforms to UTF-8. I wanted something purely to check if a string contains UTF-8 characters so that I could switch character encoding from iso-8859-1 to utf-8.

I modified the pattern to only look for non-ascii multibyte sequences in the UTF-8 range and also to stop once it finds at least one multibytes string. This is quite a lot faster.

A simple way to detect UTF-8/16/32 of file by its BOM (not work with string or file without BOM)

Function to detect UTF-8, when mb_detect_encoding is not available it may be useful.

Much simpler UTF-8-ness checker using a regular expression created by the W3C:

Just a note: Instead of using the often recommended (rather complex) regular expression by W3C (http://www.w3.org/International/questions/qa-forms-utf-8.en.php), you can simply use the ‘u’ modifier to test a string for UTF-8 validity:

For detect UTF-8, you can use:

In my environment (PHP 7.1.12),
«mb_detect_encoding()» doesn’t work
where «mb_detect_order()» is not set appropriately.

To enable «mb_detect_encoding()» to work in such a case,
simply put «mb_detect_order(‘. ‘)»
before «mb_detect_encoding()» in your script file.

Both
«ini_set(‘mbstring.language’, ‘. ‘);»
and
«ini_set(‘mbstring.detect_order’, ‘. ‘);»
DON’T work in script files for this purpose
whereas setting them in PHP.INI file may work.

beware : even if you need to distinguish between UTF-8 and ISO-8859-1, and you the following detection order (as chrigu suggests)

returns ISO-8859-1, while

bottom line : an ending ‘�’ (and probably other accentuated chars) mislead mb_detect_encoding

a) if the FUNCTION mb_detect_encoding is not available:

Sometimes mb_detect_string is not what you need. When using pdflib for example you want to VERIFY the correctness of utf-8. mb_detect_encoding reports some iso-8859-1 encoded text as utf-8.
To verify utf 8 use the following:

//
// utf8 encoding validation developed based on Wikipedia entry at:
// http://en.wikipedia.org/wiki/UTF-8
//
// Implemented as a recursive descent parser based on a simple state machine
// copyright 2005 Maarten Meijer
//
// This cries out for a C-implementation to be included in PHP core
//
function valid_1byte($char) <
if(!is_int($char)) return false;
return ($char & 0x80) == 0x00;
>

function valid_2byte($char) <
if(!is_int($char)) return false;
return ($char & 0xE0) == 0xC0;
>

function valid_3byte($char) <
if(!is_int($char)) return false;
return ($char & 0xF0) == 0xE0;
>

function valid_4byte($char) <
if(!is_int($char)) return false;
return ($char & 0xF8) == 0xF0;
>

function valid_nextbyte($char) <
if(!is_int($char)) return false;
return ($char & 0xC0) == 0x80;
>

This might be of interest when trying to convert utf-8 strings into ASCII suitable for URL’s, and such. this was never obvious for me since I’ve used locales for us and nl.

Last example for verifying UTF-8 has one little bug. If 10xxxxxx byte occurs alone i.e. not in multibyte char, then it is accepted although it is against UTF-8 rules. Make following replacement to repair it.

Replace
> // goto next char
with
> else <
return false; // 10xxxxxx occuring alone
> // goto next char

function isUTF8($str) <
if ($str === mb_convert_encoding(mb_convert_encoding($str, «UTF-32», «UTF-8»), «UTF-8», «UTF-32»)) <
return true;
> else <
return false;
>
>

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *