【问题标题】:Java, unzip folder with German characters in filenamesJava,解压缩文件名中带有德语字符的文件夹
【发布时间】:2019-05-04 00:44:34
【问题描述】:

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

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());
    }
}

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

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

【问题讨论】:

  • 如果ZIP文件不包含任何个人信息,请分享一下吗
  • 它很容易复制,只需创建一个名为 Aufhänge 的文本文件并压缩它
  • 我试过了,但我没有收到错误
  • 我用JAVA8编译
  • 我也在使用 Java 8。也许问题可能出在操作系统中。我使用的是 Windows 10。我认为在 linux 上文件名的编码方式不同。

标签: java utf-8 unzip


【解决方案1】:

您没有提及您的操作系统,也没有提及您是如何创建 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());
        }
    }

}

正如您所指出的,代码在执行此语句时抛出java.lang.IllegalArgumentException: MALFORMEDzipEntry = zipInputStream.getNextEntry()) != null

问题出现是因为默认情况下 7-Zip 使用 Cp437 对 zip 文件中的文件名进行编码,如 this comment from 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 > 添加到存档..."
  • Add to Archive 对话框的 Parameters 字段中指定 cu

  • 将压缩后的文件名存储为 UTF-8 格式后,您可以在代码中将 Charset.forName("Cp437") 替换为 Charset.forName("UTF-8"),解压时不会抛出异常。

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


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

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

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

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

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

【讨论】:

  • 谢谢,不过,我正在为公众编写客户端,这意味着一些 zip 将使用 Cp437 进行编码,而一些则使用其他编码。我是否应该只尝试一种编码,如果错误,捕获异常,然后尝试另一种,依此类推?没有更好的办法吗?
  • @LukaGorgadze 我已经更新了我的答案。如果 zip 文件是在 Windows 上创建的,则假定 UTF-8 如果设置了 通用位标志 的第 11 位,如我的更新中所述。否则假设 Cp437,但也要捕获任何IllegalArgumentException,并研究用于创建该压缩文件的编码。然后根据需要增强您的代码以适应其他编码。请参阅How can I correctly decompress a ZIP archive of files with Hebrew names? 了解更多信息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-05
  • 2016-01-10
  • 2010-12-14
  • 1970-01-01
  • 2018-11-06
  • 2013-03-09
相关资源
最近更新 更多