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

标签 java utf-8 unzip

我正在尝试解压缩其中包含德语字符的文件夹,例如 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());
    }
}

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

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

最佳答案

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

  • 创建一个包含一些琐碎内容的简单文本文件(例如,除了三个字符“abc”之外什么都没有)。
  • 将文件另存为 D:\Temp\Aufhänge.txt。请注意文件名中的变音符号。
  • 在 Windows 文件资源管理器中找到该文件。
  • 选择文件并右键单击。从上下文菜单中选择7-Zip > 添加到“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: MALFORMED:zipEntry = zipInputStream.getNextEntry()) != null

出现此问题的原因是默认情况下 7-Zip 使用 Cp437 对 zip 文件中的文件名进行编码,如 this comment from 7-Zip 中所述。 :

Default encoding is OEM (DOS) encoding. It's for compatibility with old zip software.

这就是为什么使用 Charset.forName("Cp437") 而不是 Charset.forName("UTF-8") 时解压缩有效。

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

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

    AddToArchive

  • 以 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目前不提供一种方法来存储用于压缩文件名的编码(除了一个异常(exception)),如“附录 D - 语言编码 (EFS)”中所述:

    D.2 If general purpose bit 11 is unset, the file name and comment SHOULD conform to the original ZIP character encoding. If general purpose bit 11 is set, the filename and comment MUST support The Unicode Standard, Version 4.1.0 or greater using the character encoding form defined by the UTF-8 storage specification. The Unicode Standard is published by the The Unicode Consortium (www.unicode.org). UTF-8 encoded data stored within ZIP files is expected to not include a byte order mark (BOM).

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

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

关于Java,解压缩文件名中包含德语字符的文件夹,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55393956/

相关文章:

php - UTF8 character_set_server 和 DataTables 插件

java - 如何在 Java 中解压缩目录中的所有 Zip 文件夹?

java - 将android SQLite文本内容转储到textView中

java - 无法将歌曲时长从 ms 转换为 h 格式 :m:s

Java Reflections - 使用 BigInteger 参数调用方法

apache - 在 htaccess 重写规则中验证 utf-8

ruby-on-rails - Ruby 1.8 中所有空格的 gsub 字符串

java - 谷歌登录已签名的 apk 无法正常工作

python - 如何从 Python 中的 zip 文件中读取 zip 文件?

c# - Ionic Zip 仅提取特定文件夹