@alexalexes

Как получить имя файла из zip архива, который содержит кириллицу?

Дано: php 5.6.
Архив 123.zip, созданный с помощью 7-zip в Windows с настройками по умолчанию.
Внутри архива содержится файл с именем:
Съешь ещё этих мягких французских булок, да выпей чаю.txt

Любыми архиваторами и стандартными средствами Windows архив читается и извлекается без ошибок.
С помощью модуля ZipArchive пытаемся получить имя файла в архиве:
setlocale(LC_ALL, 'ru_RU.utf8');
  header('Content-Type: text/html; charset=utf-8');
  $zip = new ZipArchive;
  $zip_temp_file_name = '123.zip';
  $zip_res = $zip->open($zip_temp_file_name);
  $inner_zip_file_name = $zip->getNameIndex(0);
  var_dump($inner_zip_file_name);

На выходе получается:
æΩÑΦ∞ ÑΘ± φΓ¿σ ¼∩ú¬¿σ Σαᡵπºß¬¿σ í𽫬, ñá óδ»Ñ⌐ τáε.txt

Как извлечь имя файла, содержащийся в архиве и вывести его в кодировке utf-8?
  • Вопрос задан
  • 171 просмотр
Решения вопроса 1
@alexalexes Автор вопроса
Имя файла, получаемое при работе с модулем ZipArchive, из-за бага в этом модуле требует некоторого преобразования через промежуточные однобайтовые кодировки.
Нужно провести такую цепочку преобразований:
UTF-8 -> encode1 -> encode2 (encode3) -> UTF-8
В php это будет так:
$out_str = iconv('UTF-8', $encode1.'//IGNORE', $in_str);
$out_str = iconv($encode1, $encode2.'//IGNORE', $out_str);
$out_str = iconv($encode3, 'UTF-8//IGNORE', $out_str);

Но проблема в том, что для каждой конфигурации сервера эти кодировки могут отличаться.
Чтобы их найти нужно воспользоваться методом перебора.
Для начала найдем все кодировки, которые поддерживает функция iconv.
Для этого в консоли сервера вызовем:
$ iconv -l
Из набора кодировок нужно взять только те, которые содержат наименование "CPxxx", где xxx - число.
С помощью скрипта провести полный перебор:
$all_encoding = []; // сюда вставляем список всех кодировок iconv
$out_encoding = array_filter($all_encodings, function($item){return strpos(strtolower($item), 'cp') !== false;});
  foreach($out_encoding as $encode1)
  {
    foreach($out_encoding as $encode2)
    {
      foreach($out_encoding as $encode3)
      {
          $str = iconv('UTF-8', $encode1.'//IGNORE', $out);
          $str = iconv($encode1, $encode2.'//IGNORE', $str);
          $str = iconv($encode3, 'UTF-8//IGNORE', $str);
          if($str !== false && $str !== '')
            echo $encode1.'::'.$encode2.'::'.$encode3.'::'.$str.'<br>';
      }
    }
  }

Визуально ищем тот вариант, где имя файла восстановилось.
В моем случае это:
UTF-8 -> cp437 -> cp437 (cp866) -> UTF-8
В коде это выглядит так:
$out_str = iconv('UTF8', 'CP437//IGNORE', $in_str);
$out_str = iconv('CP437', 'CP437//IGNORE', $out_str);
$out_str = iconv('CP866', 'UTF8//IGNORE', $out_str);
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы
YCLIENTS Москва
от 200 000 до 350 000 ₽
Ведисофт Екатеринбург
от 25 000 ₽
ИТЦ Аусферр Магнитогорск
от 100 000 до 160 000 ₽