ZipFile中的namelist()返回带有无效编码的字符串

Cro*_*oll 8 string unicode encoding python-3.x chardet

问题是,对于一些上传到python应用程序的档案或文件,它ZipFilenamelist()返回错误解码的字符串.

from zip import ZipFile
for name in ZipFile('zipfile.zip').namelist():
    print('Listing zip files: %s' % name)
Run Code Online (Sandbox Code Playgroud)

如何修复该代码,以便我总是解码unicode中的文件名(所以支持Chineeze,俄语和其他语言)?

我已经看到了Python 2的一些示例,但由于字符串的性质在python3中已经改变,我不知道如何重新编码它,或者在其上应用chardet.

bob*_*nce 7

如何修复该代码,以便我总是解码unicode中的文件名(所以支持Chineeze,俄语和其他语言)?

自动?你不能.基本ZIP文件中的文件名是字节字符串,没有附加的编码信息,因此除非您知道创建ZIP的机器上的编码是什么,否则您无法可靠地获得人类可读的文件名.

现代ZIP文件上的标志有一个扩展名,告诉您文件名是UTF-8.不幸的是,从Windows用户收到的文件通常没有,所以你会猜测像chardet这样的固有不可靠的方法.

我已经看到了Python 2的一些示例,但由于字符串的性质在python3中已经改变,我不知道如何重新编码它,或者在其上应用chardet.

Python 2只会给你原始字节.在Python 3中,新行为是:

  • 如果设置了UTF-8标志,它将使用UTF-8对文件名进行解码,并返回正确的字符串值

  • 否则,它使用DOS代码页437解码文件名,这很不可能是预期的.但是,您可以将字符串重新编码回原始字节,然后尝试使用您实际需要的代码页重新解码,例如name.encode('cp437').decode('cp1252').

不幸的是(再次,因为不幸的ZipFile事情永远不会以ZIP为中心结束),这种解码是否会在没有告诉你它做什么的情况下进行无声解码.因此,如果您想要切换并且仅在文件名可疑时执行转码步骤,则必须复制用于嗅探是否设置了UTF-8标志的逻辑:

ZIP_FILENAME_UTF8_FLAG = 0x800

for info in ZipFile('zipfile.zip').filelist():
    filename = info.filename
    if info.flag_bits & ZIP_FILENAME_UTF8_FLAG == 0:
        filename_bytes = filename.encode('437')
        guessed_encoding = chardet.detect(filename_bytes)['encoding'] or 'cp1252'
        filename = filename_bytes.decode(guessed_encoding, 'replace')
    ...
Run Code Online (Sandbox Code Playgroud)


jfs*_*jfs 7

zipfile.py以下是根据仅支持 cp437 和 utf-8 字符编码的 zip规范解码文件名的代码:

        if flags & 0x800:
            # UTF-8 file names extension
            filename = filename.decode('utf-8')
        else:
            # Historical ZIP filename encoding
            filename = filename.decode('cp437')
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,如果0x800未设置标志,即如果您的输入中未使用 utf-8,zipfile.zip则使用 utf-8,因此“Chineeze、俄语和其他语言”cp437的结果可能不正确。

实际上,可以使用 ANSI 或 OEM Windows 代码页来代替 cp437。

cp866如果您知道俄语 Windows 上可能使用的实际字符编码,例如(OEM(控制台)代码页) ,那么您可以重新编码文件名以获得原始文件名:

filename = corrupted_filename.encode('cp437').decode('cp866')
Run Code Online (Sandbox Code Playgroud)

最好的选择是使用 utf-8 创建 zip 存档,以便您可以在同一存档中支持多种语言:

c:\> 7z.exe a -tzip -mcu archive.zip <files>..
Run Code Online (Sandbox Code Playgroud)

或者

$ python -mzipfile -c archive.zip <files>..`
Run Code Online (Sandbox Code Playgroud)