Java,解压缩文件名中包含德语字符的文件夹

Luk*_*dze 3 java unzip utf-8

我正在尝试解压缩其中包含德语字符的文件夹,例如 Aufhänge 。我知道在 Java 7 中,它默认使用 utf-8,我认为“ä”是 utf-8 字符之一。这是我的代码片段

public static void main(String[] args) throws IOException {
    ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(ZIP_PATH), StandardCharsets.UTF_8);
    ZipEntry zipEntry;
    while ((zipEntry = zipInputStream.getNextEntry()) != null) {
        System.out.println(zipEntry.getName());
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我得到的错误:java.lang.IllegalArgumentException: MALFORMED

它适用于 Charset.forName("Cp437"),但不适用于 StandardCharsets.UTF_8

sko*_*isa 12

您没有提到您的操作系统,也没有提到您如何创建 zip 文件,但我还是设法在 Windows 10 上使用7-Zip重新创建了您的问题:

  • 创建一个包含一些琐碎内容的简单文本文件(例如,只有三个字符“abc”)。
  • 将文件另存为D:\Temp\Aufhänge.txt。请注意文件名中的变音符号。
  • 在 Windows 文件资源管理器中找到该文件。
  • 选择文件并右键单击。从上下文菜单中选择7-Zip > Add to "Aufhänge.zip"以创建Aufhänge.zip

然后,在 NetBeans 中运行以下代码以解压缩您刚刚创建的文件:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class GermanZip {

    static String ZIP_PATH = "D:\\Temp\\Aufhänge.zip";

    public static void main(String[] args) throws FileNotFoundException, IOException {

        ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(ZIP_PATH), Charset.forName("UTF-8"));
        ZipEntry zipEntry;
        while ((zipEntry = zipInputStream.getNextEntry()) != null) {
            System.out.println(zipEntry.getName());
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

正如你指出的那样,代码抛出java.lang.IllegalArgumentException: MALFORMED执行该语句时:zipEntry = zipInputStream.getNextEntry()) != null

出现问题是因为默认情况下7-Zip使用 Cp437 对 zip 文件中的文件名进行编码,如7-Zip 的评论中所述

默认编码为 OEM (DOS) 编码。它是为了与旧的 zip 软件兼容。

这就是为什么在使用Charset.forName("Cp437")而不是Charset.forName("UTF-8").

如果要使用解压缩,Charset.forName("UTF-8")则必须强制7-Zip以 UTF-8 对 zip 中的文件名进行编码。为此,请在运行7-Zip时指定cu参数,如链接注释中所述:

  • 在 Windows 文件资源管理器中选择文件并右键单击。
  • 从上下文菜单中选择7-Zip > 添加到存档...”
  • 在“添加到存档”对话框的“参数”字段中指定cu

    添加到档案

  • 具有存储的压缩文件名中UTF-8格式,然后你可以替换Charset.forName("Cp437")Charset.forName("UTF-8")在你的代码,并解压时也不例外将被抛出。

此答案特定于 Windows 10 和 7-Zip,但一般原则应适用于任何环境:如果为您指定 UTF-8 编码,请ZipInputStream确保 zip 文件中的文件名确实是使用 UTF-8 编码的。您可以通过在二进制编辑器中打开 zip 文件并搜索压缩文件的名称来轻松验证这一点。


根据以下 OP 的评论/问题进行更新:

  • 不幸的是,.ZIP 文件格式规范目前没有提供一种方法来存储用于压缩文件名的编码,除了一个例外,如“附录 D - 语言编码 (EFS)”中所述:

    D.2 如果通用位 11 未设置,文件名和注释应该符合原始 ZIP 字符编码。 如果设置了通用位 11,则文件名和注释必须支持使用 UTF-8 存储规范定义的字符编码形式的 Unicode 标准版本 4.1.0 或更高版本。 Unicode 标准由 The Unicode Consortium (www.unicode.org) 发布。存储在 ZIP 文件中的 UTF-8 编码数据预计不包含字节顺序标记 (BOM)。

  • 因此,在您的代码中,对于每个压缩文件,首先检查是否设置了通用位标志的第 11。如果是,那么您可以确定该压缩文件的名称是使用UTF-8编码的。否则,编码就是创建压缩文件时使用的任何编码。在 Windows 上默认是Cp437,但是如果您在 Windows 上运行并处理在 Linux 上创建的 zip 文件,我认为没有一种简单的方法可以确定所使用的编码。

  • 不幸的是ZipEntry没有提供访问压缩文件的通用位标志字段的方法,因此您需要在字节级别处理 zip 文件才能做到这一点。
  • 更复杂的是,本文中的“编码”与用于每个压缩文件名的编码有关,而不是用于 zip 文件本身。一个压缩文件名可以用UTF-8编码,另一个压缩文件名可以使用Cp437等添加。