使用 zipfile 解压含有中文文件名的 zip 文件

问题

在使用 Python 内置标准库 zipfile 解压文件时,如果压缩文件中有的文件含有中文,那么解压后就会发现文件名中的中文部分是乱码。例如我分别新建三个 txt 文件:文件1.txt文件2.txt文件3.txt,然后将这三个文件压缩到一个名为 文件.zip 的压缩文件中。然后我们使用下面的代码来解压:

import zipfile
with zipfile.ZipFile('文件.zip', 'r') as f:
    f.extractall()

解压的结果如图所示,中文已成乱码:

解压结果

原因

原因很简单,zipfile 会将所有文件名用 CP437 来编码,官方说明如下:

There is no official file name encoding for ZIP files. If you have unicode file names, you must convert them to byte strings in your desired encoding before passing them to write(). WinZip interprets all file names as encoded in CP437, also known as DOS Latin.

解决

知道文件名用的编码后,就可以使用对应的编码来解码了。也就是先用 CP437 编码 encode 成 bytes,再以 gbk 格式解码成中文 string。

有两种解决方案,两种方案都是使用 extract 方法而不是 extractall方法,都是对压缩文件内的文件名进行遍历,逐个解压。

Python 版本为 3.6.4 |Anaconda custom (64-bit)| (default, Jan 16 2018, 10:22:32) [MSC v.1900 64 bit (AMD64)]

方案 1

第一种方案的思路是:

  1. 将文件名正确解码,并用解码后的文件名创建一个新文件。
  2. 打开原文件,即文件名乱码的文件。
  3. 将原文件中的内容写入到新文件中。

对应的代码如下:

import shutil
import zipfile

with zipfile.ZipFile('文件.zip', 'r') as zf:
    for fn in zf.namelist():
        right_fn = fn.encode('cp437').decode('gbk')  # 将文件名正确编码
        with open(right_fn, 'wb') as output_file:  # 创建并打开新文件
            with zf.open(fn, 'r') as origin_file:  # 打开原文件
                shutil.copyfileobj(origin_file, output_file)  # 将原文件内容复制到新文件

方案 2

第二种方案思路就比较简单了:

  1. 正常解压文件。
  2. 使用正确的文件名重命名解压的文件。

对应的代码如下,这里使用了 pathlib 库,强烈推荐该库!

from pathlib import Path
import zipfile

with zipfile.ZipFile('文件.zip', 'r') as f:
    for fn in f.namelist():
        extracted_path = Path(f.extract(fn))
        extracted_path.rename(fn.encode('cp437').decode('gbk'))

END

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Python小屋

使用with关键字让你的Python代码更加Pythonic

首先解释一下上一篇文章Python科学计算扩展库numpy中的广播运算中最后的小题目,该题目答案是一个元组(True, 5),原因在于Python中的等号=虽然...

3588
来自专栏C/C++基础

C++覆盖或删除指定位置的文件内容

我们经常使用ofstream或者fstream可写文件,使用ifstream可以写文件,但需要设置文件的打开状态为ios::out。C++中IO流打开模式使用位...

1273
来自专栏数据结构与算法

2991:2011

2991:2011 查看 提交 统计 提问 总时间限制:1000ms内存限制:65536kB描述已知长度最大为200位的正整数n,请求出2011^n的后四位。输...

3337
来自专栏积累沉淀

干货--Redis 30分钟快速入门

一、 redis环境搭建 1.简介        redis是一个开源的key-value数据库。它又经常被认为是一个数据结构服务器。因为它的value不仅...

30810
来自专栏Petrichor的专栏

AttributeError: 'module' object has no attribute 'fullmatch'.

经过查找,发现出错的原因是 re库 中的 fullmatch函数 是 在py3.4之后才新添加的 。

2673
来自专栏C/C++基础

Linux命令(31)——find命令

find命令用于在指定目录查找文件,可以指定一些匹配条件,如按文件名、文件类型、用户甚至是时间戳来查找文件。

1234
来自专栏开源优测

接口测试 | 25 requests + pytest测试实例

概述 本文主要分享如何将pytest和requests结合一起使用,让大家有个初步的了解。 主要内容有: pytest简介 pytest + requests示...

6219
来自专栏木木玲

设计模式 ——— 职责链模式

1443
来自专栏小筱月

shell 文本操作命令

:s/old/new/g 将当前行中查找到的所有字符串“old” 替换为“new”

1192
来自专栏菩提树下的杨过

bash/shell编程学习(2)

注:linux中有一个经典名言【一切皆文件】,/dev/null可以认为是一个特殊的空文件,更形象点,可以理解为科幻片中的黑洞,任何信息重向定输出到它后,便有去...

793

扫码关注云+社区

领取腾讯云代金券