Автоопределение и автоисправление кодировки на PHP


Дорабатывая недавно сайт на не очень ровном движке, наткнулся на проблему с кодировкой строк. Выгружая данные из XML файла получал их абракадаброй и это пол беды. Часть строк необходимо было конвертировать в utf-8 а часть в CP1251. Как так получилось, известно одному создателю данного XML файла, но тем неменее работу нужно было делать. И тут меня посетила интересная мысль - написать функцию, которая сама определяет из чего во что необходимо конвертировать строку и автоматически делала это. На выходе возвращала нормальную строку в читаемом виде.

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

  • В функцию отправляется строка с битой кодировкой.
  • Внутри функции имеется массив со всеми возможными вариантами кодировок. При необходимости расширить диапазон возможных кодировок, нужно просто добавить её в массив.
  • Помимо массива кодировок имеется строка для сравнения результата (в моём случае это русский алфавит в нижнем регистре). Тут я думаю вы начинаете догадываться как работает функция.
  • Возвращает функция строку в нормальной кодировке.

Ниже приведён полный код функции:

<?
echo fix_charset('Сведения Рѕ прошлой С‚С ЂСѓРґРѕРІРѕР№ деятельности (2 последних местР');

function fix_charset($string) {
$chek_string = 'абвгдеёжзийклмнопрстуфхцчшщъыьэюя';  //  Строка для поиска совпадений
$list_charset = array("UTF-8", "ASCII", "Windows-1252", "CP1256", "CP1251"); // Список кодировок для поиска
$array_result = array();

foreach ($list_charset as $current_charset)
    {    
    foreach ($list_charset as $two_charset) 
        {
        $string_after_conversion = iconv($current_charset, $two_charset, $string);
        
        // Проверка результата и занесение данных в массив
        $matches = 0;
        $len_chek_string = strlen($chek_string);
        for ($k=0; $k<$len_chek_string; $k++)
            {
            if(eregi($chek_string[$k],$string_after_conversion)) 
        	    { 
        	    $matches++;
        	    }  
            }
        $array_result[]=array(
            'matches'=>$matches, 
            'charset_in'=>$current_charset, 
            'charset_out'=>$two_charset, 
            'result'=>$string_after_conversion
            );
        // Конец проверки  
        
        //echo '<br>'.$current_charset.' - '.$two_charset." - ".$string_after_conversion." (".$matches."/".$len_chek_string.")";
        }   
    }
  
  
//  Тут бубны для сортировки многомерного массива по полю matches
$t = call_user_func_array('array_merge_recursive', $array_result);
asort($t['matches']);
$so = array_keys($t['matches']);
asort($so);
$so = array_keys($so);
$array_result = array_combine($so, $array_result);
krsort($array_result);  
//  теперь массив отсортирован по нужному полю (число совпадений)....

/*echo '<pre>';
print_R($array_result);
echo '</pre>'; die;*/

//  Показываю последний элемент массива, с самым большим количеством совпадений символов
return $array_result[count($array_result)-1]['result'];  
}
?>

Сразу оговорюсь, получился неплохой, но все-же костыль. В моём случае он ниразу не подвел, отработал на 100%. Ещё она неплохо используется при работе ICQ шпаргалок на PHP.

Возможно функция немного ресурсоемкая, поэтому для ускорения её работы стоит уменьшить массив возможный кодировок. Это значительно увеличит скорость работы программы. Ещё можно уменьшить строку для поиска совпадений, т.к. она много раз вся перебирается в цикле, но я бы не рекомендовал, т.к. вероятность нахождения верной кодировки резко снижается.

Ещё для правильного исправления кодировки, на сервере должна быть установлена библиотека iconv. К счастью на большинстве серверов она установлена по умолчанию.

Функция правильно исправляет кодировку на PHP с вероятностью 95%.

Теперь давайте посмотрим как-же она работает.

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

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

Теперь если вы хотите исправить кодировку строки на PHP, просто скормите функции кривую строку и она вернёт вам её же в нормальном виде.

Данная функция для автоопределения кодировки работает на нескольких реальных интернет-магазинах при выгрузке XML файла. Возможно её стоит доработать чтобы она проверяла в самом начале работы какую строку ей отправили, чтоб в случае если ей отправили уже нормальную строку, она останавливалась и не грузила лишний раз сервер, но как показала практика, нормальная строка нормальной и возвращается, да и вообще что-то было лень;).

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

 


Тэги:

Комментарии: 1

Прокомментировать »

 
 
Sanasol
22.04.2015
 

Красавчик :D Очень помогло

 

Прокомментировать

 
 
Сообщение *
 
Проверочный код *
 
 
 
Яндекс.Метрика