ZipInputStream(InputStream, Charset) 解码 ZipEntry 文件名错误

7

Java 7旨在解决使用字符集不是UTF-8的zip归档文件时的旧问题。这可以通过构造函数ZipInputStream(InputStream, Charset)来实现。到目前为止,一切都好。当明确设置ISO-8859-1字符集时,我可以解压包含umlauts文件名的zip文件。

但是问题在于:当使用ZipInputStream.getNextEntry()迭代流时,条目名称中的特殊字符不正确。在我的情况下,umlaut "ü"被替换为"?"字符,这显然是错误的。有人知道如何解决吗?显然,ZipEntry忽略了其底层ZipInputStreamCharset。看起来又是JDK中与zip相关的一个bug,但我可能也做错了什么。

...
zipStream = new ZipInputStream(
    new BufferedInputStream(new FileInputStream(archiveFile), BUFFER_SIZE),
    Charset.forName("ISO-8859-1")
);
while ((zipEntry = zipStream.getNextEntry()) != null) {
    // wrong name here, something like "M?nchen" instead of "München"
    System.out.println(zipEntry.getName());
    ...
}

除了升级到SE7之外,Java SE6的最佳实践是什么? - basZero
对于SE6:我测试了将VM参数zip.altEncodingzip.encoding设置为Cp437ISO-8859-1,但两者都无法正确读取。 - basZero
@basZero:Apache Commons Compress工作得很好。但我没有找到现成的解决方案。 - kriegaex
1个回答

10

我试了两个多小时,但就在我最终在这里发布问题的五分钟后,我碰巧找到了答案:我的zip文件没有使用ISO-8859-1编码,而是使用了Cp437。因此构造函数调用应该是:

zipStream = new ZipInputStream(
    new BufferedInputStream(new FileInputStream(archiveFile), BUFFER_SIZE),
    Charset.forName("Cp437")
);

现在它就像魔法一样运行。


我认为你可以接受这个答案是正确的,即使它是你自己写的,根据这篇文章:http://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/。 - seh
我有同样的问题,花了我几个小时才解决。解决方法非常简单,只需为我使用MS-DOS编码cp852而不是win cp1250。 - Perlos
是的,那就是同样的问题和解决方案,只不过不是针对英语MS-DOS代码页437,而是针对中欧代码页852。当然,确切的解决方案始终取决于生成所涉及的环境和工具ZIP存档。 - kriegaex
1
Java的行为可以说是不符合规范的,因为规范似乎非常清楚,当“语言编码标志(EFS)”未设置时,Cp437是默认值。“D.1 ZIP格式历史上仅支持原始IBM PC字符编码集,通常称为IBM代码页437.... D.2 如果未设置通用位11,则文件名和注释应符合原始ZIP字符编码。”https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT - mrg
我点赞了你的评论,因为链接是一个非常有用的资源。公平地说,Java并不声称尝试检测编码甚至读取EFS,但明确记录了它使用UTF-8作为默认值,这在现今是可以理解的,特别是因为它也是JAR文件的默认值。所以在Java中,在调用ZipInputStream构造函数之前,你必须知道编码。很公平。你的评论之所以如此有帮助,是因为知道Cp437实际上是一个默认值,所以在出现任何问题时,这应该是要尝试的第一个编码之一。 - kriegaex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接