杰瑞科技汇

Python zipfile 如何处理中文文件名?

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

Python zipfile 如何处理中文文件名?-图1
(图片来源网络,侵删)

核心问题:ZIP 文件名编码不统一

问题根源在于 ZIP 规范的历史演变:

  1. 传统 ZIP (ZIP-32):在 Windows 和许多旧系统中,默认使用系统编码来存储文件名,在中文 Windows 上,这通常是 gbk 编码,在 macOS 或 Linux 上,可能是 utf-8 或其他编码。
  2. 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 文件创建时使用的编码,直接指定它即可。

Python zipfile 如何处理中文文件名?-图2
(图片来源网络,侵删)

示例代码:

假设你有一个名为 中文测试.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 编码写入文件名

Python zipfile 如何处理中文文件名?-图3
(图片来源网络,侵删)

解决方案:使用 zipfile.ZIP_STOREDZIP_DEFLATED 并确保字符串是 Unicode

在 Python 3 中,字符串默认就是 Unicode,当你调用 writestrwrite 时,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 文件中提取中文文件名

这本质上是场景一的延伸,读取文件后,你还需要处理文件路径。

解决方案:

  1. 使用 encoding 参数正确读取 ZIP 文件中的文件名列表。
  2. 将文件名作为 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-8cp437
创建 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 参数。
分享:
扫描分享到社交APP
上一篇
下一篇