ZipFile.infolist()返回顺序与压缩包实际文件顺序不符的解决办法咨询
ZipFile.infolist()返回顺序与压缩包实际文件顺序不符的解决办法咨询
问题描述
我最近在实现一个功能:提取压缩包内的文件,并将它们重命名为从1到n的序列(保留原文件后缀)。但遇到了一个头疼的问题——当压缩包内的原文件名本身是按1、2、3...n命名时,ZipFile.infolist()返回的文件顺序完全不符合预期!
我最初写的代码是这样的:
from zipfile import ZipFile with ZipFile(file) as zf: for i, file_info in enumerate(zf.infolist(), 1): # 提取原文件后缀并重新命名 ext = file_info.filename.split(".")[-1] file_info.filename = f'{i}.{ext}' zf.extract(file_info, file_path)
结果调试时发现,压缩包里的文件本来是按1、2、3、4...n的顺序排列,但infolist()返回的ZipInfo列表却是按字符串字典序排序的——也就是1、10、11、12...2、3、4...这种顺序,直接导致我重命名后的文件顺序完全错乱了。
我想到过一个临时方案:让压缩包里的原文件名改成01、02、03这种带前导零的格式,但这个方案太依赖原始文件的命名规范,不够通用可靠。
解决方案
这个问题的根源在于:ZipFile.infolist()返回的顺序是文件在压缩包内部存储的顺序,而很多压缩工具(比如Windows默认的压缩功能)在添加数字命名的文件时,会按字符串字典序来添加,而非我们直观的数字大小顺序。所以我们需要手动对infolist()的结果进行排序,按照文件名的数字大小重新排列。
这里分两种常见场景给出解决代码:
场景1:文件名是纯数字+后缀(比如1.jpg、2.png)
直接提取文件名中的数字部分转成整数,以此作为排序依据:
from zipfile import ZipFile def extract_and_rename(file, file_path): with ZipFile(file) as zf: # 按文件名的数字大小排序ZipInfo列表 sorted_files = sorted(zf.infolist(), key=lambda x: int(x.filename.split(".")[0])) for i, file_info in enumerate(sorted_files, 1): ext = file_info.filename.split(".")[-1] file_info.filename = f'{i}.{ext}' zf.extract(file_info, file_path)
场景2:文件名包含其他字符但核心是数字(比如img_1.jpg、doc_2.pdf)
用正则表达式提取文件名中的数字部分,再进行排序:
import re from zipfile import ZipFile def extract_and_rename(file, file_path): with ZipFile(file) as zf: def get_file_number(file_info): # 匹配文件名中的连续数字 match = re.search(r'\d+', file_info.filename) return int(match.group()) if match else 0 # 无数字的文件排到最后 # 按提取到的数字大小排序 sorted_files = sorted(zf.infolist(), key=get_file_number) for i, file_info in enumerate(sorted_files, 1): ext = file_info.filename.split(".")[-1] file_info.filename = f'{i}.{ext}' zf.extract(file_info, file_path)
补充说明
- 如果你能控制压缩包的生成过程,也可以在打包时确保文件是按数字大小顺序添加的,但这种方式远不如手动排序灵活通用。
- 不同压缩工具的文件存储逻辑可能有差异,但手动按数字排序的方案能覆盖绝大多数场景。
备注:内容来源于stack exchange,提问作者Murad Rustemzade
相关产品推荐
相关产品推荐

