我使用 python 的 zipfile 模块提取 .zip 存档(让我们以 http://img.dafont.com/dl/?f=akvaleir 处的此文件为例。)
f = zipfile.ZipFile('akvaleir.zip', 'r')
for fileinfo in f.infolist():
print fileinfo.filename
f.extract(fileinfo, '.')
其输出:
Akval�ir_Normal_v2007.ttf
Akval�ir, La police - The Font - Fr - En.pdf
这两个文件在提取后都无法访问,因为它们的文件名中存在无效的编码字符。问题是 zipfile 模块没有指定输出文件名的选项。
但是,“unzip akvaleir.zip”很好地转义了文件名:
root@host:~# unzip akvaleir.zip
Archive: akvaleir.zip
inflating: AkvalВir_Normal_v2007.ttf
inflating: AkvalВir, La police - The Font - Fr - En.pdf
我尝试在 python 程序中捕获“unzip -l akvaleir.zip”的输出,这两个文件名是:
Akval\xd0\x92ir_Normal_v2007.ttf
Akval\xd0\x92ir, La police - The Font - Fr - En.pdf
如何在不捕获“unzip -l akvaleir.zip”输出的情况下获得正确的文件名,就像 unzip 命令所做的那样?
花了一些时间,但我想我找到了答案。
我认为这个词应该是 Akvaléir。我找到了有关此内容的法语页面描述。当我使用你的代码片段时,我有一个像
这样的字符串>>> fileinfo.filename
'Akval\x82ir, La police - The Font - Fr - En.pdf'
>>>
这不适用于 UTF8、Latin-1、CP-1251 或 CP-1252 编码。然后我发现 CP863 可能是加拿大编码,所以这可能来自法语加拿大。
>>> print unicode(fileinfo.filename, "cp863").encode("utf8")
Akvaléir, La police - The Font - Fr - En.pdf
>>>
但是,我随后阅读了 Zip 文件格式规范,上面写着
ZIP 格式历史上 仅支持原装 IBM PC 字符编码集,常见 称为 IBM 代码页 437。
...
如果设置了通用位 11,则 文件名和注释必须支持 Unicode 标准,版本 4.1.0 或 更大程度地使用字符编码 由UTF-8存储定义的形式 规格。
测试给出的答案与加拿大代码页相同
>>> print unicode(fileinfo.filename, "cp437").encode("utf8")
Akvaléir, La police - The Font - Fr - En.pdf
>>>
我没有 Unicode 编码的 zip 文件,并且我不会创建一个来查找,所以我假设所有 zip 文件都具有 cp437 编码。
import shutil
import zipfile
f = zipfile.ZipFile('akvaleir.zip', 'r')
for fileinfo in f.infolist():
filename = unicode(fileinfo.filename, "cp437")
outputfile = open(filename, "wb")
shutil.copyfileobj(f.open(fileinfo.filename), outputfile)
在我的 Mac 上
109936 Nov 27 01:46 Akvale??ir_Normal_v2007.ttf
25244 Nov 27 01:46 Akvale??ir, La police - The Font - Fr - En.pdf
哪个选项卡完成
ls Akvale\314\201ir
并在我的文件浏览器中显示一个漂亮的“é”。
我在使用 Docker 运行应用程序时遇到了类似的问题。将这一行添加到 Dockerfile 中,为我解决了所有问题:
RUN locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
所以,我想如果您不使用 Docker,请尝试一下并确保正确生成和设置语言环境。
我改编了上面的代码,并添加了一些来自 ftfy 的 unicode 清理(强烈推荐)。 也许这个版本有帮助。
import shutil
import zipfile
import ftfy
import os
f = zipfile.ZipFile('./Portugal-20241125T151942Z-001.zip', 'r')
for fileinfo in f.infolist():
filename = ftfy.fix_text(fileinfo.filename)
directory = os.path.dirname(filename)
if directory and not os.path.exists(directory):
os.mkdir(directory)
with open(filename,"wb") as fout:
shutil.copyfileobj(f.open(fileinfo.filename), fout)