Comments 82
скорее баян — так шифровки дешифровывали еще при царе горохе.
но, это самый единственный идеологический правильный метод. еще правильнее только атака по словарю, но она намного более ресурсоемка.
Конечно баян. Немного упрощенный, чтобы быть менее ресурсоемким, но в то же время еще работающим. Только вот таких баянов я еще не встречал для детекта кодировок — все пытаются через коды символов узнавать кодировку
Помню когда-то давно была проблема, что какой-то ИЕ отправлял данные на сервер, через ajax, в неверной кодировке. И так пробовал и этак, всё равно неверно, хоть убейся. Так вот, использовал костыль с добавлением параметра только для ИЕ и далее на php проверял. Если есть параметр, менял кодировку данных post. Самый костылистый костыль.
Проблема решалась формированием ответа запроса в виде полноценного xhtml с meta encoding. Сам в свое время столкнулся. ИЕ стал вести себя прилично, а остальные браузеры как работали ранее, так и работали дальше.
Не только ослик, но и сафари. Ибо utf8! Либо вы правильно работаете с кодировками (хедеры, меты и т.д.), либо используете утф. Это не проблема браузера, это с вами проблема.
Вы тут все такие умные капитаны. Написал же, что давно проблема была, а вы так яростно бросились объяснять очевидные вещи.
Я бы рекомендовал использовать биграммы (триграммы) вместе с вероятностью их появления в тексте. Мне в одной из подобных проблем давал более точные результаты.
уверен, что N-граммы дадут более точный результат.
Похожий подход используется в одной из ссылок для детекта языков, но ИМХО для детекта кодировок это уже несколько избыточно.
очевидно, что N-граммы дадут верный ответ с большей вероятностью, но калькуляция занимает больше времени, в то время как в случае биграмм имхо (естесственно не производил реальных замеров) в несколько раз повышается точность за счет небольшого увеличения времени исполнения.
в свое время тоже необходимо было решить такую задачу.
По-моему находил где-то решение с ngram на php
Однако, если уж автор залез внутрь php, не составит труда залезть внутрь firefox и выковырять оттуда модуль определения кодировок
Решение с n-граммами на php есть по ссылке в топике (в разделе «поиск по хабру» вторая ссылка). Действительно на мой взгляд очень хорошее решение проблемы. Просто ИМХО несколько избыточно — для детекта языка в самый раз, а кодировки попроще можно детектить.
Только что увидел Ваш ответ у себя в почте. Ваше решение очень интересное, однако (мое мнение) как Вы считаете правильно ли использовать такие тексты как «Война и Мир» (я бы тоже использовал что-то похожее или тексты lib.ru)?
Можете быть правильно использовать свежие новости?
Если сравнивать не частоты появления в тексте символов, а частоты пар, троек или большего количества символов идущих подряд, то определение будет гораздо точнее работать. Пар вполне достаточно. Где-то в сети и библиотеки такие быть должны.
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;
}
сделайте substr() на строке в UTF-8, как иногда делают в тайтлах некоторых сайтов — и все, iconv() сразу споткнется с ворнингом.
Есть, но проблему уже порезанной строки это не решает.

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

Один только я думаю, что приложение на PHP обязано работать при error_reporting(E_ALL) без ошибок, ворнингов и нотисов, а на продакшене просто нужно его выключать на всякий случай?
Это обоснование моего личного мнения насчет этой функции. И почему она не работает на 100%
а вы замеряли время работы своей функции по сравнению с иконв, и его ворнингом?
Пожалуйста, прочитайте обновление в посте про эту функцию.
Кроме того, что она генерирует ворнинги, она еще и не работает, поэтому измерять гипотетическую производительность смысла не вижу.
Понимаете, была задача, определить текст в УТФ8 или в цп1251, функция работает 100%, без отказов.
Теперь скажите реальный пример определения огромного количества кодировок?
Не совсем согласен. Функция фактически определяет, строка в 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.
тогда, задача сократилась к определению утф8, а всё остальное цп1251, да признаю.
за новый пример спасибо.
зы: на будущее не будь таким высокомерным, эта штука по жизни не очень помогает ;)
Спасибо за «зы».
Иногда замечаю такое за собой, перечитывая что уже написал. Приму к сведению, постараюсь исправиться.
Большое вам спасибо, знай я этот метод раньше — мог бы сэкономить полдня бесплодных усилий:)
Я для краулера использовал librcd, либа давала очень хорошие результаты и работало шустро (принцип работы видимо тот же). Написал обертку к php и пользовал.
В качестве другого костыля можно использовать PSpell — проверять по словарю текст в разных кодировках, где ошибок меньше — та кодировка и верна.
Вопрос не перехода, а работы с данными в неизвестной кодировке.
Да как раз хотел предложить с помощью Swig сгенерить php-модуль для enca и использовать его. Но вы же знаете PHP-шников :)
ну в крайнем случае через можно ведь и через шел запустить :)
Ага. А потом каждый раз при деплойменте сношаться с админами вражеских серверов, чтобы они установили/разрешили устанвливать свои расширения. Зачем усложнять и так достаточно простую задачу? 10-15 строк на PHP + несколько массивов данных из 15-30 элементов для каждой кодировки — и зачем генерировать php-модули, перегруженные функционалом для определения японских, корейских, китайских кодировок, которые большинству никогда и не понадобятся?
в большинстве интернетов и прочих оффлайнов оно уже вполне наступило же )
Это только в России никак не наступит. Так как два байта на букву ппц как много и вообще. Это как с аськой и жж — все уже забыли кроме русских. Контактов видите ли много там полезных.
это только подтверждает мнение о том, что пхп язык для дураков
раз даже стандартную билиотеку написали с такими вопиющими нарушениями
это просто смешно товарищи
а есть такое мнение оказывается?

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

вы же вряд ли в сорсу когда нибудь лазили?
озвучьте их.

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

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

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

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

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

Частотное распределение имеет ошибку зависящую от количества символов и до 50 и даже до 100 она достаточно большая. Если надо распознавать фразу или даже несколько слов, то надо делать распределение или веса пары символов.
Согласен. Как по мне, описанный в статье метод нельзя использовать для произвольных текстов. Он подходит только для определения относительно длинных текстов, для текстов из 3 слов он, выражаясь словами автора, убожество.
Прежде чем критиковать, не мешало бы внимательно прочитать статью и посмотреть примеры.
Мои замеры в примерах на текстах из трех слов приведены в статье — все работает, как ни странно, даже на таких коротких текстах.
По большому счёту когда приходят данные в неизвестной кодировке важно определить — это UTF или нет, в противном случае принимаем волевое решение что это codepage 1251. На извращенцев, по недоразумению до сих пор использующих КОИ-как, EBCDIC или Mazowia можно забить имхо. А определить UTF не легко а очень легко: if (preg_match('//u', $string))…
Насчет определения UTF-8 — согласен (http://habrahabr.ru/blogs/php/107945/#comment_3413195)
Насчет забить на все не-cp1251 кодировки — к сожалению, не могу себе такого позволить. Да и не только я, мне кажется.
В одном из проектов тоже столкнулся с проблемой определения кодировки приходящего текста.
В багтрекере 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, тогда на надёжность работы уже полагаться не придётся. Надо бы проверить.
Статистика, сэр))
Спасибо что проделали такой обьем работы!!! Тема — более чем актуальна!
Вы плохо искали ;)

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;
Интересное решение, спасибо. Пугает немного только непонятный алгоритм
Only those users with full accounts are able to leave comments. Log in, please.