查看: 197|回复: 1

Python解压ZIP中文乱码:自动识别GBK/UTF-8及嵌套递归解压脚本

[复制链接]
发表于 2 小时前 | 显示全部楼层 |阅读模式
在跨平台传输 ZIP 压缩包时,Python 内置 zipfile 模块解压出的中文文件名经常变成乱码或问号,尤其从 Windows 传到 Linux(含麒麟)或 macOS 后最明显。这个问题的根源不在代码逻辑,而在于 ZIP 协议的历史编码缺陷——ZIP 标准最初使用 CP437 编码存储文件名(不支持中文),而不同系统压缩时使用的实际编码又不统一:Windows 默认 GBK/GB2312,Linux/macOS/麒麟默认 UTF-8。Python 的 zipfile 读取时始终按 CP437 解码,导致中文字符错乱。

针对这一痛点,业界主要有三种解决思路:Python 3.11+ 内置的 metadata_encoding 参数可一行搞定,但版本受限;手动转码方案通过 CP437→GBK/UTF-8 逆向修复,全版本通用且稳定;第三方库 patoolib 依赖系统原生解压工具,适合临时场景但缺乏环境独立性。下面提供一套基于手动转码优化、可自动识别编码并递归解压嵌套 ZIP 包的通用脚本,兼容所有 Python 版本和主流操作系统,包括国产麒麟系统。
  1. # -*- coding: utf-8 -*-
  2. """
  3. Python ZIP压缩包万能解压脚本
  4. 功能:自动识别GBK/UTF-8编码、修复中文文件名乱码、支持嵌套ZIP压缩包递归解压
  5. 适配:全Python版本、Windows/麒麟Linux/macOS全平台
  6. """
  7. import zipfile
  8. import os
  9. def unzip_file_auto_encoding(zip_path: str, output_root: str) -> None:
  10.     """
  11.     通用解压函数,自动识别编码、处理中文乱码、递归解压嵌套zip
  12.     :param zip_path: 待解压的zip文件绝对/相对路径
  13.     :param output_root: 文件解压输出根目录
  14.     """
  15.     os.makedirs(output_root, exist_ok=True)
  16.     with zipfile.ZipFile(zip_path, "r") as zf:
  17.         for file_name in zf.namelist():
  18.             real_file_name = ""
  19.             # 自动编码识别:优先GBK,失败则尝试UTF-8
  20.             try:
  21.                 real_file_name = file_name.encode("cp437").decode("gbk")
  22.             except UnicodeDecodeError:
  23.                 real_file_name = file_name.encode("cp437").decode("utf-8")
  24.             full_file_path = os.path.join(output_root, real_file_name)
  25.             # 处理目录
  26.             if file_name.endswith("/"):
  27.                 os.makedirs(full_file_path, exist_ok=True)
  28.             else:
  29.                 # 先解压原始乱码文件,再重命名为正确中文名
  30.                 zf.extract(file_name, output_root)
  31.                 os.rename(os.path.join(output_root, file_name), full_file_path)
  32.             # 递归解压嵌套压缩包
  33.             if real_file_name.lower().endswith(".zip"):
  34.                 nest_output_dir = os.path.join(output_root, real_file_name[:-4])
  35.                 unzip_file_auto_encoding(full_file_path, nest_output_dir)
  36.                 # 可选:解压后删除嵌套压缩包,按需开启
  37.                 # os.remove(full_file_path)
  38. if __name__ == "__main__":
  39.     TARGET_ZIP = "test.zip"   # 待解压的压缩包路径
  40.     OUTPUT_DIR = "unzip_out"  # 解压输出目录
  41.     unzip_file_auto_encoding(TARGET_ZIP, OUTPUT_DIR)
  42.     print(f"✅ 解压完成!所有文件已输出至:{os.path.abspath(OUTPUT_DIR)}")
复制代码

脚本的核心逻辑是先将 zipfile 读取的原始文件名(按 CP437 编码)重新编码为字节,再尝试用 GBK 解码。由于 Windows 压缩包通常使用 GBK,这个优先尝试成功率高;如果抛出 UnicodeDecodeError,立即切换为 UTF-8 解码,适配 Linux/macOS/ 麒麟等系统。这样无需用户手动指定编码,自动修复乱码。

在处理嵌套 ZIP 包时,脚本会检查解压出的每一个文件是否以 .zip 结尾,如果是则递归调用自身,并将嵌套包解压到该文件名去后缀的子目录中,避免文件覆盖和混淆。例如 test.zip 内包含 archive.zip,解压后会生成 unzip_out/archive/ 目录,其中放置 archive.zip 内所有文件。

如果你的 Python 环境是 3.11 及以上版本,可以使用官方新增的 metadata_encoding 参数简化代码:
  1. import zipfile
  2. with zipfile.ZipFile("test.zip", "r", metadata_encoding="gbk") as zf:
  3.     zf.extractall("unzip_out")
  4. # 若为UTF-8编码压缩包,将metadata_encoding="utf-8"
复制代码
但注意此方式无法自动切换编码,需要预先知道压缩包的编码,且仅限 Python 3.11+。

第三方库 patoolib 虽然方便,但依赖系统安装的 unzip(Linux)或 7-Zip/WinRAR(Windows),在服务器或无界面的轻量化环境里不建议使用。安装命令为 pip install patool,调用方式为 patoolib.extract_archive("test.zip", outdir="unzip_out")。

实际使用时,建议将通用脚本作为默认解压工具,因为它不依赖任何第三方库,兼容所有 Python 版本和操作系统,也完美适配国产麒麟 Linux 环境。只需修改脚本末尾的 TARGET_ZIP 和 OUTPUT_DIR 路径即可集成到项目中。注意嵌套解压后原压缩包默认保留,若需自动删除可解除注释 os.remove 行。
回复

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: Python解压ZIP中文乱码:自动识别GBK/UTF-8及嵌套递归解压脚本

这个脚本太实用了!之前用zipfile解压Windows传过来的包总是出现乱码,手动指定编码又容易搞错,你这个自动识别GBK/UTF-8并递归解压嵌套包的设计正好解决了痛点。特别是那个先用CP437编码回字节再尝试GBK或UTF-8解码的思路,逻辑清晰又通用,加上对麒麟系统的适配很贴心。回头我就把项目里的解压函数换成这个,省心多了👍
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-6-12 11:58 , Processed in 0.031091 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部