Во время работы над импортом данных из csv для сервиса дзен-мани передо мной встала задача автоматического определения кодировки содержащихся в загруженном пользователем файле данных.
Поискав в гугле варианты решения, я с удивлением обнаружил что наиболее продвинутый алгоритм основан на использовании словарей недопустимых комбинаций символов. Мне это показалось не самым оптимальным решением и я написал собственную функцию определения кодировки.
Смысл алгоритма довольно-таки прост, он основывается на следующих наблюдениях:
таким образом были выведены два критерия, по которым можно было оценивать успешность подбора кодировки:
в результате реализации был получен следующий код:
Поискав в гугле варианты решения, я с удивлением обнаружил что наиболее продвинутый алгоритм основан на использовании словарей недопустимых комбинаций символов. Мне это показалось не самым оптимальным решением и я написал собственную функцию определения кодировки.
Смысл алгоритма довольно-таки прост, он основывается на следующих наблюдениях:
- Соотношение гласных к согласным примерно одинаково для всех текстов одного и того же языка (для русского — около 1 к 3)
- В неправильно закодированном тексте это соотношение нарушается
- В неправильно закодированном тексте уменьшается количество национальных символов
таким образом были выведены два критерия, по которым можно было оценивать успешность подбора кодировки:
- количество национальных символов в тексте должно приближаться к количеству символов в тексте
- соотношение национальных гласных к согласным должно приближаться к частотным характеристикам языка
в результате реализации был получен следующий код:
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 = '/(а|у|е|ы|о|э|ю|и|я)/i';
$reg2 = '/(й|ц|к|н|г|ш|щ|з|х|ъ|ф|в|п|р|л|д|ж|б|ь|т|м|с|ч)/i';
$mk = 1000000;
$enc = 'ascii';
foreach ($list as $item)
{
$sample1 = @iconv($item, 'UTF-8', $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;
}
* This source code was highlighted with Source Code Highlighter.
Параметры:
- $string — строка в неизвестной кодировке.
- $pattern_size — если строка больше этого размера, то определение кодировки будет производиться по шаблону из $pattern_size символов, взятых из середины переданной строки. Это сделано для увеличения производительности на больших текстах.
Как всегда — автор не несёт никакой ответственности за использование этого кода ;)
Советы и замечания очень приветствуются. Спасибо.