【问题标题】:Shell - Unzip folder with accentShell - 解压带重音的文件夹
【发布时间】:2023-08-21 20:12:01
【问题描述】:

我在 Windows 中创建了一个具有这种结构的“.zip”:

myfile.zip
    - trénsfèst
        - file1.png
        - file2.png
        - file3.png

使用 PHP,我发送一个 shell_exec 以将 myfile.zip 放在我的服务器上。在我的 shell 文件中,我需要解压缩此文件以获取特定文件夹中的结构。当我执行unzip myfile.zip 时,所有的重音都没有被解释:

Archive:  myfile.zip
creating: tr?n'sf?rt/
inflating: tr?n'sf?rt/file1.png
inflating: tr?n'sf?rt/file2.png
inflating: tr?n'sf?rt/file3.png

当我尝试删除文件夹时,有一些方块代替了重音符号。有没有解决方案来解压缩我的所有重音文件夹

谢谢

【问题讨论】:

  • 你能以某种方式分享文件吗?我想我知道如何解决它,但我想在发布答案之前检查解决方案。
  • 只需在 windows 上创建一个带重音符号的文件夹(是否包含文件)并使用 winrar 或 7zip 压缩它。该文件不具体。
  • 问题在于 Zip 中的文件名编码取决于系统区域设置。在不同的 Windows 设置上,结果可能会有所不同。如果您希望快速解决问题,请分享文件。
  • 您将在我的 Windows 上解决问题,但我的应用程序的所有用户都可以放入 .zip,因此我无法将文件发送给您。是的,我认为问题来自 lcoale。

标签: php shell zip unzip


【解决方案1】:

Windows 通常根据语言环境对文件名进行编码。例如,对于俄语设置,它通常将文件名编码为 CP866。文件名以相同的语言环境放入 Zip 中,即取决于创建存档的系统的语言环境。

检测编码

几年前我tried to solve this problem 得出的结论是,总的来说,没有办法可靠地检测编码。在 PHP 中,您可以尝试使用 ZipArchivemb_detect_encoding

$zip = new ZipArchive;
$filename = $argv[1];

if (! $zip->open($filename))
  die("failed to open $filename\n");

for ($i = 0; $i < $zip->numFiles; ++$i) {
  $encoding = mb_detect_encoding($zip->getNameIndex($i), 'auto');
  if (! $encoding) {
    trigger_error("Failed to detect encoding for " . $zip->getNameIndex($i), E_USER_ERROR);
    exit(1);
  }
  $zip->renameIndex($i, iconv($encoding, 'UTF-8', $zip->getNameIndex($i)));
}
$zip->extractTo('/home/ruslan/tmp/unzippped/');
$zip->close();

但根据我的经验,mb_detect_encoding 不是很准确。

您可以尝试使用enca工具检测编码如下:

ls -1 folder | enca -L ru

其中ru 是语言代码(所有语言代码都可通过enca --list languages 获得)。但这需要您猜测语言。要将文件名从一种编码实际转换为 UTF-8,您可以使用enconv,例如:

ls -1 folder | enconv -L russian -x UTF-8

但是,您需要再次猜测语言。

所以我建议尝试使用上述方法之一检测编码,并要求用户从所有可用编码的列表中选择编码。默认情况下,可能会在列表中选择自动检测的编码。就个人而言,我选择让用户在没有智能自动检测的情况下选择编码。

当您知道源编码时

Unzip 支持带有-p 选项的管道流。但它仅适用于批量数据。也就是说,它不会将流分成将所有未压缩内容传递给程序的文件:

解压 -p foo | more => 将 foo.zip 的内容通过管道发送到程序 more

解析原始流显然是一项艰巨的任务。一种方法是将文件提取到目录中,然后使用如下脚本转换文件名:

$path = $argv[1];
$from_encoding = isset($argv[2]) ? $argv[2] : 'CP866';

if ($handle = opendir($path)) {
  while ($file = readdir($handle)) {
    rename($file, iconv($from_encoding, 'UTF-8', $file));
  }
  closedir($handle);
}

示例用法:

php script.php directory Windows-1252

或者,使用ZipArchive,如下所示。

$zip = new ZipArchive;

$filename = $argv[1];
$from_encoding = isset($argv[2]) ? $argv[2] : 'CP866';

$zip->open($filename) or die "failed to open $filename\n";

for ($i = 0; $i < $zip->numFiles; ++$i) {
  $zip->renameIndex($i, iconv($from_encoding,'UTF-8', $zip->getNameIndex($i)));
}
$zip->extractTo('/target/directory/');

$zip->close();

示例用法:

php script.php file.zip Windows-1252

【讨论】:

    【解决方案2】:

    感谢 Ruslan Osmanov,但我找到了解决方案。 解压缩我的 zip 文件后,我使用 convmv 所以这是我的过程:

    unzip myfile.zip
    convmv --notest -r -f WINDOWS-1252 -t utf8
    

    感谢这篇文章:Windows-1252 to UTF-8 encoding

    【讨论】:

    • 我的答案包含 convmv 顺便说一句。此外,您不能断言它始终是 Windows-1252,因为它取决于源语言环境。最后,由于您的问题被标记为 php,因此我使用 ziparchive 和 iconv 的解决方案更合适。