Cro*_*oll 8 string unicode encoding python-3.x chardet
问题是,对于一些上传到python应用程序的档案或文件,它ZipFile会namelist()返回错误解码的字符串.
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.
如何修复该代码,以便我总是解码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)
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)