在 python 中使用特殊字符解压缩时使用编码修复文件名

Fix filenames with encoding when unzipping with special characters in python

那里有很多关于编码的问题,但我还是没能解决我的问题。

假设我在一个压缩的 ZIP 文件中包含三个文件:

Übersicht.pdf finalePräsentation münchen

我想解压缩这些文件,所以我这样做了:

with zipfile.ZipFile("path/result.zip", "r") as zip_ref:
    zip_ref.extractall("/path/")

文件名看起来很垃圾:

我的研究表明文件名基本上是字节串,OS 不可能看到编码是什么。但我仍然想知道是否有任何方法可以纠正文件名问题,以便正确显示德语“Umlaute”。

我试过这样更改编码:

    with zipfile.ZipFile(save_as, "r") as zip_ref:
        print(zip_ref.namelist())
        encoded_strings = [s.encode("utf-8") for s in zip_ref.namelist()]
        print(encoded_strings)
        zip_ref.extractall(dest)

我用 latin-1iso 和其他一些编码试过这个,实际上字节字符串的解释不同,但总是很神秘。因此,我问这个问题是为了看看是否有一种简单的方法可以解决这个问题。

非常感谢,非常感谢帮助

编辑:locale 的输出给我以下内容:

LANG="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_CTYPE="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_ALL="en_US.UTF-8"
第一个文件开头的

hexdump 如下所示:

0000000 25 50 44 46 2d 31 2e 34 0a 25 93 8c 8b 9e 20 52
0000010 65 70 6f 72 74 4c 61 62 20 47 65 6e 65 72 61 74
0000020 65 64 20 50 44 46 20 64 6f 63 75 6d 65 6e 74 20
0000030 68 74 74 70 3a 2f 2f 77 77 77 2e 72 65 70 6f 72

回声*.pdf | xxd |头给我这个:

00000000: 6669 6e61 6c65 5072 c3a4 7365 6e74 6174  finalePr..sentat
00000010: 696f 6e2e 7064 660a                      ion.pdf.
00000000: 6dc3 bc6e 6368 656e 2e70 6466 0a         m..nchen.pdf.
00000000: c39c 6265 7273 6963 6874 2e70 6466 0a    ..bersicht.pdf.

如果您找不到原始编码,您可以随时尝试使用以下方法回退到 ascii:

[unicodedata.normalize('NFKD', s).encode('ascii', 'ignore') for s in zip_ref.namelist()]

使用内置库 unicodedata

感谢十六进制转储。使用更新后的数据,文件名似乎完全是 运行 of the mill mojibake using probably code page 1252.

destination_file = filename.encode('cp1252').decode('utf-8')

我在您更新您的问题之前的最初推测保留在下面,因为它可能很有趣和/或具有指导意义。


您的屏幕截图有点模糊,但文件名隐约看起来像是编码为 Windows code page 437

>>> import unicodedata
>>> unicodedata.normalize('NFKD', "Übersicht.pdf").encode('utf-8')
b'U\xcc\x88bersicht.pdf'

正在检查字符代码 0xcc 它转换为字形 ╠ (U+2560) 在编码 cp1125、cp437、cp720、cp737、cp775、cp850、cp852、cp855、cp856、cp857、cp858、cp860、cp861、cp862、cp863、cp865、cp866 和 cp869 中; 和 0x88 translates to ê‎ (U+00EA 在 cp437、cp720、cp850、cp857、cp858、cp860、cp861、cp863 和 cp865 中。交集有多种编码,但 437 是迄今为止发明 PKzip 时最常见的编码。

(╠是双笔画的,而你的截图看起来更像单笔画的版本,但这可能只是字体设计的问题and/or图片不清晰;结论足够有说服力我要这样做。)

(披露:链接指向我自己的页面。)

假设这个分析是正确的,并且假设 zip 库给你的名字是字节串,你应该能够用

简单地解码它们
destination_file = filename.encode('latin-1').decode('cp437')

绕过 Latin-1 模糊地将每个字符代码转换为相应的字节值(回想一下,Latin-1 在前 256 个字符中与 Unicode 兼容,但它是一种纯 8 位字符编码)所以我们然后可以通过使用正确的编解码器对其进行解码将其映射回 Unicode。