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

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

    1. Почему не mb_detect_encoding() ?


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

    Давайте смотреть:
    // На входе - русский текст в кодировке CP1251
    $string = iconv('UTF-8', 'Windows-1251', 'Он подошел к Анне Павловне, поцеловал ее руку, подставив ей свою надушенную и сияющую лысину, и покойно уселся на диване.');
    
    // Посмотрим, что нам выдает md_detect_encoding(). Сначала $strict = FALSE
    var_dump(mb_detect_encoding($string, array('UTF-8')));
    // UTF-8
    var_dump(mb_detect_encoding($string, array('UTF-8', 'Windows-1251')));
    // Windows-1251
    var_dump(mb_detect_encoding($string, array('UTF-8', 'KOI8-R')));
    // KOI8-R
    var_dump(mb_detect_encoding($string, array('UTF-8', 'Windows-1251', 'KOI8-R')));
    // FALSE
    var_dump(mb_detect_encoding($string, array('UTF-8', 'ISO-8859-5')));
    // ISO-8859-5
    var_dump(mb_detect_encoding($string, array('UTF-8', 'Windows-1251', 'KOI8-R', 'ISO-8859-5')));
    // ISO-8859-5
    
    // Теперь $strict = TRUE
    
    var_dump(mb_detect_encoding($string, array('UTF-8'), TRUE));
    // FALSE
    var_dump(mb_detect_encoding($string, array('UTF-8', 'Windows-1251'), TRUE));
    // FALSE
    var_dump(mb_detect_encoding($string, array('UTF-8', 'KOI8-R'), TRUE));
    // FALSE
    var_dump(mb_detect_encoding($string, array('UTF-8', 'Windows-1251', 'KOI8-R'), TRUE));
    // FALSE
    var_dump(mb_detect_encoding($string, array('UTF-8', 'ISO-8859-5'), TRUE));
    // ISO-8859-5
    var_dump(mb_detect_encoding($string, array('UTF-8', 'Windows-1251', 'KOI8-R', 'ISO-8859-5'), TRUE));
    // ISO-8859-5
    

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

    Чтобы окончательно развеять все надежды на использование mb_detect_encoding(), надо залезть в исходники расширения mbstring. Итак, закатали рукава, поехали:
    // ext/mbstring/mbstring.c:2629
    PHP_FUNCTION(mb_detect_encoding)
    {
    ...
    // строка 2703
    ret = mbfl_identify_encoding_name(&string, elist, size, strict);
    ...
    

    Ctrl + клик:
    // ext/mbstring/libmbfl/mbfl/mbfilter.c:643
    const char*
    mbfl_identify_encoding_name(mbfl_string *string, enum mbfl_no_encoding *elist, int elistsz, int strict)
    {
    	const mbfl_encoding *encoding;
    
    	encoding = mbfl_identify_encoding(string, elist, elistsz, strict);
    ...
    

    Ctrl + клик:
    // ext/mbstring/libmbfl/mbfl/mbfilter.c:557
    /*
     * identify encoding
     */
    const mbfl_encoding *
    mbfl_identify_encoding(mbfl_string *string, enum mbfl_no_encoding *elist, int elistsz, int strict)
    {
    ...
    

    Постить полный текст метода не буду, чтобы не засорять статью лишними исходниками. Кому это интересно посмотрят сами. Нас истересует строка под номером 593, где собственно и происходит проверка того, подходит ли символ под кодировку:
    // ext/mbstring/libmbfl/mbfl/mbfilter.c:593
    (*filter->filter_function)(*p, filter);
    if (filter->flag) {
    	bad++;
    }
    

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

    Windows-1251 (оригинальные комментарии сохранены)
    // ext/mbstring/libmbfl/filters/mbfilter_cp1251.c:142
    /* all of this is so ugly now! */
    static int mbfl_filt_ident_cp1251(int c, mbfl_identify_filter *filter)
    {
    	if (c >= 0x80 && c < 0xff)
    		filter->flag = 0;
    	else
    		filter->flag = 1; /* not it */
    	return c;	
    }
    


    KOI8-R
    // ext/mbstring/libmbfl/filters/mbfilter_koi8r.c:142
    static int mbfl_filt_ident_koi8r(int c, mbfl_identify_filter *filter)
    {
    	if (c >= 0x80 && c < 0xff)
    		filter->flag = 0;
    	else
    		filter->flag = 1; /* not it */
    	return c;	
    }
    


    ISO-8859-5 (тут вообще все весело)
    // ext/mbstring/libmbfl/mbfl/mbfl_ident.c:248
    int mbfl_filt_ident_true(int c, mbfl_identify_filter *filter)
    {
    	return c;
    }
    

    Как видим, 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. Поиск по хабру


    1) опять коды символов: habrahabr.ru/blogs/php/27378/#comment_710532

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

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


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

    Регистрозависимый «спектр»:
    array (
      'о' => 0.095249209893009,
      'е' => 0.06836817536026,
      'а' => 0.067481298384992,
      'и' => 0.055995027400041,
      'н' => 0.052242744063325,
    ....
      'э' => 0.002252892226507,
      'Н' => 0.0021318391371162,
      'П' => 0.0018574762967903,
      'ф' => 0.0015961610948418,
      'В' => 0.0014044332975731,
      'О' => 0.0013188987793209,
      'А' => 0.0012623590130186,
      'К' => 0.0011804488387602,
      'М' => 0.001061932790165,
    )
    


    Регистронезависимый:
    array (
      'О' => 0.095249209893009,
      'о' => 0.095249209893009,
      'Е' => 0.06836817536026,
      'е' => 0.06836817536026,
      'А' => 0.067481298384992,
      'а' => 0.067481298384992,
      'И' => 0.055995027400041,
      'и' => 0.055995027400041,
    ....
      'Ц' => 0.0029893589260344,
      'ц' => 0.0029893589260344,
      'щ' => 0.0024649163501406,
      'Щ' => 0.0024649163501406,
      'Э' => 0.002252892226507,
      'э' => 0.002252892226507,
      'Ф' => 0.0015961610948418,
      'ф' => 0.0015961610948418,
    )
    


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

    Windows-1251: case sensitive, case insensitive
    KOI8-R: case sensitive, case insensitive
    ISO-8859-5: case sensitive, case insensitive

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

    $encodings = array(
    	'cp1251' => require 'specter_cp1251.php',
    	'koi8r' => require 'specter_koi8r.php',
    	'iso88595' => require 'specter_iso88595.php'
    );
    $enc_rates = array();
    for ($i = 0; $i < len($str); ++$i)
    {
    	foreach ($encodings as $encoding => $char_specter)
    	{
    		$enc_rates[$encoding] += $char_specter[ord($str[$i])];
    	}
    }
    var_dump($enc_rates);
    

    Даже не пытайтесь выполнить этот код у себя — он не заработает. Можете считать это псевдокодом — я опустил детали, чтобы не загромождать статью. $char_specter — это как раз те массивы, на которые стоят ссылки на pastebin.

    Результаты

    Строки таблицы — кодировка текста, столбцы — содержимое массива $enc_rates.

    1) $str = 'Русский текст';
    cp1251 | koi8r | iso88595 |
    0.441 | 0.020 | 0.085 | Windows-1251
    0.049 | 0.441 | 0.166 | KOI8-R
    0.133 | 0.092 | 0.441 | ISO-8859-5

    Все отлично. Реальная кодировка имеет уже в 4 раза бОльший рейтинг, чем остальные — это на таком коротком тексте. На более длинных текстах соотношение будет примерно таким же.

    2) $str = ' СТРОКА КАПСОМ РУССКИЙ ТЕКСТ';
    cp1251 | koi8r | iso88595 |
    0.013 | 0.705 | 0.331 | Windows-1251
    0.649 | 0.013 | 0.201 | KOI8-R
    0.007 | 0.392 | 0.013 | ISO-8859-5


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

    1) $str = 'Русский текст';
    cp1251 | koi8r | iso88595 |
    0.477 | 0.342 | 0.085 | Windows-1251
    0.315 | 0.477 | 0.207 | KOI8-R
    0.216 | 0.321 | 0.477 | ISO-8859-5


    2) $str = ' СТРОКА КАПСОМ РУССКИЙ ТЕКСТ';
    cp1251 | koi8r | iso88595 |
    1.074 | 0.705 | 0.465 | Windows-1251
    0.649 | 1.074 | 0.201 | KOI8-R
    0.331 | 0.392 | 1.074 | ISO-8859-5


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

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


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

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

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

    Вот код:
    $str_cp1251 = iconv('UTF-8', 'Windows-1251', 'Русский текст');
    
    var_dump(md5($str_cp1251));
    var_dump(md5(iconv('Windows-1251', 'Windows-1251', $str_cp1251)));
    var_dump(md5(iconv('KOI8-R', 'KOI8-R', $str_cp1251)));
    var_dump(md5(iconv('ISO-8859-5', 'ISO-8859-5', $str_cp1251)));
    var_dump(md5(iconv('UTF-8', 'UTF-8', $str_cp1251)));
    

    что на выходе:
    m00t@m00t:~/workspace/test$ php detect_encoding.php 
    string(32) "96e14d7add82668414ffbc498fcf2a4e"
    string(32) "96e14d7add82668414ffbc498fcf2a4e"
    string(32) "96e14d7add82668414ffbc498fcf2a4e"
    string(32) "96e14d7add82668414ffbc498fcf2a4e"
    PHP Notice:  iconv(): Detected an illegal character in input string in /home/m00t/workspace/test/detect_encoding.php on line 36
    PHP Stack trace:
    PHP   1. {main}() /home/m00t/workspace/test/detect_encoding.php:0
    PHP   2. iconv() /home/m00t/workspace/test/detect_encoding.php:36
    string(32) "d41d8cd98f00b204e9800998ecf8427e"
    

    Что мы видим? Однобайтовая кириллица после iconv($encoding, $encodigng) не изменится. Так можно отличить только UTF-8 от не-UTF-8. И то — ценой ворнинга.
    ИМХО именно вот из-за таких кусков кода и считают PHP «языком для дураков» (с) — как не переминут написать тролли в любом топике про этот язык.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 82
    • +15
      Всем костылям костыль.
      • +7
        скорее баян — так шифровки дешифровывали еще при царе горохе.
        но, это самый единственный идеологический правильный метод. еще правильнее только атака по словарю, но она намного более ресурсоемка.
        • +1
          Конечно баян. Немного упрощенный, чтобы быть менее ресурсоемким, но в то же время еще работающим. Только вот таких баянов я еще не встречал для детекта кодировок — все пытаются через коды символов узнавать кодировку
      • –3
        Помню когда-то давно была проблема, что какой-то ИЕ отправлял данные на сервер, через ajax, в неверной кодировке. И так пробовал и этак, всё равно неверно, хоть убейся. Так вот, использовал костыль с добавлением параметра только для ИЕ и далее на php проверял. Если есть параметр, менял кодировку данных post. Самый костылистый костыль.
        • 0
          Проблема решалась формированием ответа запроса в виде полноценного xhtml с meta encoding. Сам в свое время столкнулся. ИЕ стал вести себя прилично, а остальные браузеры как работали ранее, так и работали дальше.
          • 0
            Не только ослик, но и сафари. Ибо utf8! Либо вы правильно работаете с кодировками (хедеры, меты и т.д.), либо используете утф. Это не проблема браузера, это с вами проблема.
            • +1
              Вы тут все такие умные капитаны. Написал же, что давно проблема была, а вы так яростно бросились объяснять очевидные вещи.
          • +3
            Я бы рекомендовал использовать биграммы (триграммы) вместе с вероятностью их появления в тексте. Мне в одной из подобных проблем давал более точные результаты.
            • +1
              уверен, что N-граммы дадут более точный результат.
              Похожий подход используется в одной из ссылок для детекта языков, но ИМХО для детекта кодировок это уже несколько избыточно.
              • 0
                очевидно, что N-граммы дадут верный ответ с большей вероятностью, но калькуляция занимает больше времени, в то время как в случае биграмм имхо (естесственно не производил реальных замеров) в несколько раз повышается точность за счет небольшого увеличения времени исполнения.
                • 0
                  в свое время тоже необходимо было решить такую задачу.
                  По-моему находил где-то решение с ngram на php
                  Однако, если уж автор залез внутрь php, не составит труда залезть внутрь firefox и выковырять оттуда модуль определения кодировок
                  • 0
                    Решение с n-граммами на php есть по ссылке в топике (в разделе «поиск по хабру» вторая ссылка). Действительно на мой взгляд очень хорошее решение проблемы. Просто ИМХО несколько избыточно — для детекта языка в самый раз, а кодировки попроще можно детектить.
                    • 0
                      Только что увидел Ваш ответ у себя в почте. Ваше решение очень интересное, однако (мое мнение) как Вы считаете правильно ли использовать такие тексты как «Война и Мир» (я бы тоже использовал что-то похожее или тексты lib.ru)?
                      Можете быть правильно использовать свежие новости?
            • +1
              Исправьте по тексту «закасали рукава».
              • +4
                на что исправить подскажите, пожалуйста
                • +4
                  ну хотя бы на «закатали рукава»
                  • +1
                    спасибо, поправил.
              • +4
                Модуль chardet — питоновский порт the auto-detection code in Mozilla и как это всё работает A composite approach to language/encoding detection.
                я бы там посмотрел как реализовано
                • 0
                  Если сравнивать не частоты появления в тексте символов, а частоты пар, троек или большего количества символов идущих подряд, то определение будет гораздо точнее работать. Пар вполне достаточно. Где-то в сети и библиотеки такие быть должны.
                  • 0
                    habrahabr.ru/blogs/php/107945/#comment_3411483

                    тут об этом написано. Полностью согласен, но считаю полученную точность вполне удовлетворительной ).
                    • +1
                      Всё уже сказано до нас. Хоть и не начинай писать. :)
                      • 0
                        Так появился постмодернизм.
                    • +1
                      Хех, недолго осталось до наивного Байесовского классификатора.
                      • +1
                        интересная библиотека charset_x_win и метод определения кодировки ivr.webzone.ru/articles/defcod_2/index.htm
                        • +1
                          Кстати метод в данной библиотеки мне показался более интересным чем в статье.
                          Советую сбегать, почитать.
                          • 0
                            Крутая штука, кстати. Жаль напрямую в юникод не умеет
                          • +7
                            function get_encoding($str){
                                $cp_list = array('utf-8', 'windows-1251');
                                foreach ($cp_list as $k=>$codepage){
                                    if (md5($str) === md5(iconv($codepage, $codepage, $str))){
                                        return $codepage;
                                    }
                                }
                                return null;
                            }
                            • 0
                              сделайте substr() на строке в UTF-8, как иногда делают в тайтлах некоторых сайтов — и все, iconv() сразу споткнется с ворнингом.
                              • 0
                                Для utf-8 есть mb_substr().
                                • 0
                                  Есть, но проблему уже порезанной строки это не решает.

                                  PS. Способ хороший, но когда заранее знаешь что на входе валидные данные, иначе нотисы в логах начнут регулярно радовать.
                                  • 0
                                    А как так строка станет вдруг порезанной? Если мы её изначально только через mb_ обрабатывали?
                                    • –1
                                      Данные, бывает, берут не только из своей системы. Есть такая штука, как интеграция.
                                  • +1
                                    К сожалению, не все сайтописатели знают об этой функции.
                                • 0
                                  чем она отличается, от функции с моего блога?
                                  который здесь идёт как пример «убожества», которое работает 100%
                                  • 0
                                    iconv() очень привередливый. Чуть только невалидный контент — и все.
                                    Да и вообще — одному мне кажется, что делать через ошибки iconv() на неправильных кодировках нельзя?

                                    Один только я думаю, что приложение на PHP обязано работать при error_reporting(E_ALL) без ошибок, ворнингов и нотисов, а на продакшене просто нужно его выключать на всякий случай?
                                    • 0
                                      я наверное что-то пропустил, но к чему этот ответ?
                                      • 0
                                        Это обоснование моего личного мнения насчет этой функции. И почему она не работает на 100%
                                        • 0
                                          а вы замеряли время работы своей функции по сравнению с иконв, и его ворнингом?
                                          • 0
                                            Пожалуйста, прочитайте обновление в посте про эту функцию.
                                            Кроме того, что она генерирует ворнинги, она еще и не работает, поэтому измерять гипотетическую производительность смысла не вижу.
                                            • 0
                                              Понимаете, была задача, определить текст в УТФ8 или в цп1251, функция работает 100%, без отказов.
                                              Теперь скажите реальный пример определения огромного количества кодировок?
                                              • +2
                                                Не совсем согласен. Функция фактически определяет, строка в utf8 или не в utf8. И работает 100% без отказов если на входе 100% валидная строка. То же самое намного проще сделает preg_match('#.#u'):
                                                $str_utf8 = 'Русский текст';
                                                $str_cp1251 = iconv('UTF-8', 'Windows-1251', $str_utf8);
                                                var_dump(preg_match('#.#u', $str_utf8));
                                                var_dump(preg_match('#.#u', $str_cp1251));
                                                

                                                m00t@m00t:~/workspace/test$ php detect_encoding.php 
                                                int(1)
                                                int(0)
                                                

                                                причем без ворнингов.

                                                Задача в посте же ставилась — определять кодировку текста. Однобайтовых кириллических кодировок больше одной, поэтому и функция эта тут немного не в тему, мне кажется. Огромного количества кодировок не надо — достаточно почти всегда и двух однобайтовых кроме utf8 — cp1251 и koi8-r.
                                                • +1
                                                  тогда, задача сократилась к определению утф8, а всё остальное цп1251, да признаю.
                                                  за новый пример спасибо.
                                                  зы: на будущее не будь таким высокомерным, эта штука по жизни не очень помогает ;)
                                                  • 0
                                                    Спасибо за «зы».
                                                    Иногда замечаю такое за собой, перечитывая что уже написал. Приму к сведению, постараюсь исправиться.
                                                  • 0
                                                    К сожалению, не 100% результат
                                                    • 0
                                                      Большое вам спасибо, знай я этот метод раньше — мог бы сэкономить полдня бесплодных усилий:)
                                        • 0
                                          Да, вы правы, это оно, ссылку просмотрел, когда читал.
                                          • 0
                                            См обновление поста про эту функцию
                                            • 0
                                              Спасибо за грамотный разнос
                                              • 0
                                                Ловите плюсик за адкеватность )
                                        • +1
                                          Я для краулера использовал librcd, либа давала очень хорошие результаты и работало шустро (принцип работы видимо тот же). Написал обертку к php и пользовал.
                                          • 0
                                            В качестве другого костыля можно использовать PSpell — проверять по словарю текст в разных кодировках, где ошибок меньше — та кодировка и верна.
                                          • +2
                                            Вот что люди только не придумают, чтобы на юникод не перейти
                                            • +1
                                              Вопрос не перехода, а работы с данными в неизвестной кодировке.
                                              • 0
                                                Перейдёшь тут
                                              • 0
                                                Хм, интересно, а как оно в enca реализовано…
                                                • 0
                                                  Это намек кстати ;)
                                                  • 0
                                                    Да как раз хотел предложить с помощью Swig сгенерить php-модуль для enca и использовать его. Но вы же знаете PHP-шников :)
                                                    • 0
                                                      ну в крайнем случае через можно ведь и через шел запустить :)
                                                      • 0
                                                        Ага. А потом каждый раз при деплойменте сношаться с админами вражеских серверов, чтобы они установили/разрешили устанвливать свои расширения. Зачем усложнять и так достаточно простую задачу? 10-15 строк на PHP + несколько массивов данных из 15-30 элементов для каждой кодировки — и зачем генерировать php-модули, перегруженные функционалом для определения японских, корейских, китайских кодировок, которые большинству никогда и не понадобятся?
                                                  • +1
                                                    Скорей бы уже наступило светлое юникодное будущее)
                                                    • 0
                                                      в большинстве интернетов и прочих оффлайнов оно уже вполне наступило же )
                                                      • 0
                                                        Это только в России никак не наступит. Так как два байта на букву ппц как много и вообще. Это как с аськой и жж — все уже забыли кроме русских. Контактов видите ли много там полезных.
                                                      • –7
                                                        это только подтверждает мнение о том, что пхп язык для дураков
                                                        раз даже стандартную билиотеку написали с такими вопиющими нарушениями
                                                        это просто смешно товарищи
                                                        • 0
                                                          а есть такое мнение оказывается?

                                                          таких приколов полно в библиотеках любых языков, начиная с библиотек языков, на которых эти языки и библиотеки для них были написаны.

                                                          вы же вряд ли в сорсу когда нибудь лазили?
                                                          • –2
                                                            я лазил в сорсу
                                                            и я знаю софт, либы сорсами которого можно восхищаться
                                                            • +2
                                                              озвучьте их.

                                                              например либы-аналоги mb_detect_encoding.

                                                              мы перенесем их в язык для дураков.

                                                              кстати, mb_string — не «стандартная» библиотека, а так, «еще одна библиотека»… написана азиатами с оглядкой на их С/J/K
                                                        • +2
                                                          По поводу статьи и кодировки выкинуть этот модуль надо mb_detect одинаково не работает
                                                          как для кирилических кодировок также и не работает для азиатских…

                                                          Было дело Китайскую и корейскую кодировку распозновал всегда как японскую =)

                                                          www.mozilla.org/projects/intl/UniversalCharsetDetection.html Это Теория на тему композитного распознования языков, по сути что автор сделал в статье подобный метод, частоты встречания символов.

                                                          • +2
                                                            Частотное распределение имеет ошибку зависящую от количества символов и до 50 и даже до 100 она достаточно большая. Если надо распознавать фразу или даже несколько слов, то надо делать распределение или веса пары символов.
                                                            • +1
                                                              Согласен. Как по мне, описанный в статье метод нельзя использовать для произвольных текстов. Он подходит только для определения относительно длинных текстов, для текстов из 3 слов он, выражаясь словами автора, убожество.
                                                              • 0
                                                                Прежде чем критиковать, не мешало бы внимательно прочитать статью и посмотреть примеры.
                                                                Мои замеры в примерах на текстах из трех слов приведены в статье — все работает, как ни странно, даже на таких коротких текстах.
                                                                • –1
                                                                  сравните производительность вашего убожества и других
                                                            • +2
                                                              По большому счёту когда приходят данные в неизвестной кодировке важно определить — это UTF или нет, в противном случае принимаем волевое решение что это codepage 1251. На извращенцев, по недоразумению до сих пор использующих КОИ-как, EBCDIC или Mazowia можно забить имхо. А определить UTF не легко а очень легко: if (preg_match('//u', $string))…
                                                              • 0
                                                                Насчет определения UTF-8 — согласен (http://habrahabr.ru/blogs/php/107945/#comment_3413195)
                                                                Насчет забить на все не-cp1251 кодировки — к сожалению, не могу себе такого позволить. Да и не только я, мне кажется.
                                                            • 0
                                                              В одном из проектов тоже столкнулся с проблемой определения кодировки приходящего текста.
                                                              В багтрекере PHP даже баг заведён по этому поводу: bugs.php.net/bug.php?id=38138

                                                              А для проверки кодировки текста я бы рекоммендовал использовать функцию mb_check_encoding(). Она работает достаточно адекватно. По крайней мере мои проверки (UTF-8, Windows-1251, KOI8-R, ISO-8859-5) не выявили проблем. В принципе на основе этой функции также можно написать небольшой велосипедик по определению самых распространённых кодировок.

                                                              Внутрь исходников расширения mbstring не заглядывал, но теперь стало интересно как работает вышеназванная mb_check_encoding. Вполне возможно там могут использоваться те же функции что и в md_detect_encoding, тогда на надёжность работы уже полагаться не придётся. Надо бы проверить.
                                                              • 0
                                                                Статистика, сэр))
                                                                Спасибо что проделали такой обьем работы!!! Тема — более чем актуальна!
                                                                • 0
                                                                  Вы плохо искали ;)

                                                                  forum.dklab.ru/viewtopic.php?t=37830

                                                                  принцип работы — тот же:

                                                                  function detect_encoding($string, $pattern_size = 50)
                                                                  {
                                                                      $list = array('cp1251', 'utf-8', 'ascii', '855', 'KOI8R', 'ISO-IR-111', 'CP866', 'KOI8U');
                                                                      $c = strlen($string);
                                                                      if ($c > $pattern_size)
                                                                      {
                                                                          $string = substr($string, floor(($c - $pattern_size) /2), $pattern_size);
                                                                          $c = $pattern_size;
                                                                      }
                                                                  
                                                                      $reg1 = '/(\xE0|\xE5|\xE8|\xEE|\xF3|\xFB|\xFD|\xFE|\xFF)/i';
                                                                      $reg2 = '/(\xE1|\xE2|\xE3|\xE4|\xE6|\xE7|\xE9|\xEA|\xEB|\xEC|\xED|\xEF|\xF0|\xF1|\xF2|\xF4|\xF5|\xF6|\xF7|\xF8|\xF9|\xFA|\xFC)/i';
                                                                  
                                                                      $mk = 10000;
                                                                      $enc = 'ascii';
                                                                      foreach ($list as $item)
                                                                      {
                                                                          $sample1 = @iconv($item, 'cp1251', $string);
                                                                          $gl = @preg_match_all($reg1, $sample1, $arr);
                                                                          $sl = @preg_match_all($reg2, $sample1, $arr);
                                                                          if (!$gl || !$sl) continue;
                                                                          $k = abs(3 - ($sl / $gl));
                                                                          $k += $c - $gl - $sl;
                                                                          if ($k < $mk)
                                                                          {
                                                                              $enc = $item;
                                                                              $mk = $k;
                                                                          }
                                                                      }
                                                                      return $enc;
                                                                  
                                                                  • 0
                                                                    Интересное решение, спасибо. Пугает немного только непонятный алгоритм
                                                                    • 0
                                                                              $k = abs(3 - ($sl / $gl));
                                                                              $k += $c - $gl - $sl;
                                                                              if ($k < $mk)
                                                                              {
                                                                                  $enc = $item;
                                                                                  $mk = $k;
                                                                              }
                                                                      
                                                                    • 0
                                                                      Раньше отправилось, сорри
                                                                    • 0
                                                                      Если у нас из вариантов только cp1251 и utf-8, то можно просто посчитать все русские буквы
                                                                      https://gist.github.com/luchaninov/a8469ce649c8428cd02be793165f260a

                                                                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.