zipfile 模块本身是 Python 的标准库,用于处理 ZIP 归档文件,它在处理文件名(特别是非 ASCII 字符,如中文)时,会遇到一些常见的“坑”,主要原因是 ZIP 文件格式对文件名编码的规定不统一。

核心问题:ZIP 文件名编码不统一
问题根源在于 ZIP 规范的历史演变:
- 传统 ZIP (ZIP-32):在 Windows 和许多旧系统中,默认使用系统编码来存储文件名,在中文 Windows 上,这通常是
gbk编码,在 macOS 或 Linux 上,可能是utf-8或其他编码。 - ZIP64 规范:后来的 ZIP 规范(ZIP64)推荐使用 UTF-8 编码来存储文件名,以实现更好的跨平台兼容性。
这就导致了同一个 ZIP 文件,在不同系统上用 zipfile 打开时,Python 可能会以不同的编码去解读文件名,从而产生乱码。
读取 ZIP 文件中的中文文件名
这是最常见的问题,当你尝试从一个 ZIP 文件中获取文件列表,或者读取一个中文命名的文件时,可能会看到类似 b'\xB9\xE3\xD6\xDD\xCA\xA1\xCA\xA0\xCA\xA1.txt' 这样的字节串,或者直接是乱码的 Unicode 字符串。
解决方案:指定正确的编码
zipfile.ZipFile 对象有一个 encoding 参数,用于在读取文件名时指定编码。如果知道 ZIP 文件创建时使用的编码,直接指定它即可。

示例代码:
假设你有一个名为 中文测试.zip 的文件,它是在中文 Windows 系统上创建的,文件名是 gbk 编码的。
import zipfile
import os
# 假设我们有一个中文文件 '你好世界.txt'
# 先创建一个示例 ZIP 文件
with zipfile.ZipFile('中文测试.zip', 'w', compression=zipfile.ZIP_DEFLATED) as zf:
zf.writestr('你好世界.txt', '这是中文内容')
zf.writestr('data.csv', 'name,age\n张三,25')
# --- 问题演示 ---
print("--- 问题演示 (不指定编码) ---")
with zipfile.ZipFile('中文测试.zip', 'r') as zf:
# 尝试直接打印文件名,可能会乱码
print("文件列表:", zf.namelist())
# 输出可能是: ['\xB9\xE3\xD6\xDD\xCA\xA1\xCA\xA0\xCA\xA1.txt', 'data.csv']
# 或者在新版 Python 中可能自动猜对,但不稳定
# --- 解决方案 ---
print("\n--- 解决方案 (指定 gbk 编码) ---")
try:
with zipfile.ZipFile('中文测试.zip', 'r', encoding='gbk') as zf:
# namelist() 返回的是已经解码好的字符串列表
file_list = zf.namelist()
print("文件列表:", file_list)
# 输出: ['你好世界.txt', 'data.csv']
# 现在可以安全地访问中文命名的文件
with zf.open('你好世界.txt') as f:
content = f.read().decode('gbk') # 文件内容也需要用正确的编码解码
print("文件内容:", content)
# 输出: 这是中文内容
except UnicodeDecodeError:
print("指定的编码 'gbk' 不正确,请尝试其他编码,如 'utf-8'。")
except FileNotFoundError:
print("文件 '中文测试.zip' 未找到。")
如何确定编码?
- ZIP 文件是你自己创建的:你知道它是在什么系统上创建的,从而知道编码。
- ZIP 文件是别人给的:可以尝试常见的编码,如
gbk,gb18030,utf-8,big5等。 cp437:这是 ZIP 规范中提到的“原始”编码,如果你尝试gbk失败,可以试试cp437,它有时能“暴力”解码出正确的中文。
创建包含中文文件名的 ZIP 文件
在创建 ZIP 文件时,为了确保最大的跨平台兼容性,最佳实践是显式地使用 UTF-8 编码写入文件名。

解决方案:使用 zipfile.ZIP_STORED 或 ZIP_DEFLATED 并确保字符串是 Unicode
在 Python 3 中,字符串默认就是 Unicode,当你调用 writestr 或 write 时,zipfile 模块会自动将文件名编码为 UTF-8(前提是你没有使用 allowZip64=False 等限制)。
示例代码:
import zipfile
# 要添加到 ZIP 文件中的中文文件名
chinese_filename = '报告_2025年.docx'
file_content = '这是一份年度报告。'
# 创建一个新的 ZIP 文件
with zipfile.ZipFile('新文件.zip', 'w', compression=zipfile.ZIP_DEFLATED) as zf:
# 直接传入 Unicode 字符串,zipfile 会自动处理编码
zf.writestr(chinese_filename, file_content)
print(f"已将 '{chinese_filename}' 添加到 ZIP 文件中。")
# --- 验证 ---
print("\n--- 验证新生成的 ZIP 文件 ---")
with zipfile.ZipFile('新文件.zip', 'r', encoding='utf-8') as zf:
# 使用 utf-8 编码读取,可以正确显示
print("文件列表:", zf.namelist())
# 输出: ['报告_2025年.docx']
with zf.open(chinese_filename) as f:
print("文件内容:", f.read().decode('utf-8'))
# 输出: 这是一份年度报告。
关键点:
- Python 3 的字符串:确保你使用的文件名字面量是 Unicode 字符串(在代码中直接写
中文即可)。 zipfile的默认行为:现代zipfile在创建 ZIP64 文件(默认)时,会自动将文件名编码为 UTF-8,这是最推荐的方式。
从 ZIP 文件中提取中文文件名
这本质上是场景一的延伸,读取文件后,你还需要处理文件路径。
解决方案:
- 使用
encoding参数正确读取 ZIP 文件中的文件名列表。 - 将文件名作为 Unicode 字符串使用,确保你的操作系统和 Python 脚本环境能正确处理它。
示例代码:
import zipfile
import os
# 假设 '中文测试.zip' 已存在
# 创建一个用于解压的目录
extract_dir = '解压出的文件'
if not os.path.exists(extract_dir):
os.makedirs(extract_dir)
print(f"正在从 '中文测试.zip' 解压到 '{extract_dir}' 目录...")
try:
# 使用正确的编码打开 ZIP 文件
with zipfile.ZipFile('中文测试.zip', 'r', encoding='gbk') as zf:
# extractall 会自动处理文件名,前提是编码正确
zf.extractall(path=extract_dir)
print("解压成功!")
# 也可以逐个文件提取
# for filename in zf.namelist():
# print(f"正在解压: {filename}")
# source = zf.open(filename)
# target = open(os.path.join(extract_dir, filename), "wb")
# with source, target:
# target.write(source.read())
except Exception as e:
print(f"解压失败: {e}")
# 检查解压结果
print("\n解压后的文件列表:")
for item in os.listdir(extract_dir):
print(item)
# 输出:
# 你好世界.txt
# data.csv
总结与最佳实践
| 场景 | 问题 | 最佳解决方案 |
|---|---|---|
| 读取 ZIP | 中文文件名乱码 | 使用 with zipfile.ZipFile('file.zip', 'r', encoding='gbk') as zf:。gbk 不行,尝试 utf-8 或 cp437。 |
| 创建 ZIP | 生成的 ZIP 文件在其他系统上中文文件名乱码 | 在 Python 3 中,直接传入 Unicode 字符串给 writestr/write。 zipfile 会自动使用 UTF-8 编码,这是最兼容的方式。 |
| 提取 ZIP | 提取失败或文件名乱码 | 结合读取和提取:用正确的 encoding 打开 ZIP 文件,然后调用 extractall(),它会自动处理已解码的文件名。 |
| 调试 | 不知道 ZIP 文件用什么编码 | 尝试 utf-8 (现代标准)。尝试 gbk (中文 Windows 环境)。尝试 cp437 (ZIP 规范原始编码,有时能“猜”对)。 |
核心思想:
- 编码是桥梁:始终明确你在哪个环节(读取、写入)需要编码/解码。
- UTF-8 是王道:对于新创建的文件,强制使用 UTF-8 编码文件名,可以解决 90% 的跨平台问题。
- 处理旧文件:对于历史遗留的 ZIP 文件,你可能需要根据其来源猜测并尝试不同的
encoding参数。
