Python批量Excel转CSV时最后一个文件未关闭被锁定问题
问题产生原因
这个文件锁定问题是Windows平台下Python处理文件时的典型场景,核心诱因有3个:
- 垃圾回收机制的延迟特性:原代码循环末尾写的
file.close()和file = '',只是手动切断了变量对ExcelFile对象的引用,但Python的垃圾回收不是实时触发的——循环全部执行完后,最后一轮创建的ExcelFile对象不会被立刻回收,底层读取引擎持有的文件句柄没有被销毁,Windows系统会默认该文件仍被进程占用,阻止移动、删除操作。 - 不同读取引擎的close实现不兼容:pandas处理Excel依赖不同的第三方引擎(xlrd处理旧版xls、openpyxl处理xlsx、pyxlsb处理xlsb),部分版本的引擎(尤其是旧版pyxlsb)的
close()方法存在实现缺陷,调用后不会立刻释放文件锁,存在句柄泄漏问题。 - 异常场景下的资源泄漏:如果某个工作表读取、CSV写入过程中抛出未捕获的异常,后续的
file.close()不会被执行,也会导致文件被锁定,该场景属于偶发诱因。
修复方案
从资源管理的逻辑入手可以彻底解决这个问题,不需要修改系统配置:
- 优先用
with上下文管理器创建ExcelFile对象,上下文管理器会在代码块退出(哪怕抛出异常)时自动、强制释放文件资源,不需要手动调用close(),从语法层面避免句柄泄漏。 - 每次处理完单个Excel文件后,显式调用垃圾回收接口,强制销毁无引用的对象,即时释放文件句柄。
- 替换手动拼接路径、手动替换后缀的逻辑,用标准库的路径处理方法减少异常概率。
修复后的可直接运行代码如下:
import sys import gc import csv import re from os import listdir from os.path import isfile, join, splitext import pandas as pd def removeEspecialCharacters(obj): if isinstance(obj, str) : retorno = re.sub('[(\x90|\x8F)]','',obj).replace("\r","").replace("\n","") else: retorno = obj return retorno myFolder = r'C:\Users\myuser\Downloads\ConvertFilesToCsv' myFiles = [f for f in listdir(myFolder) if isfile(join(myFolder, f))] for file_name in myFiles: lower_name = file_name.lower() if not (lower_name.endswith('.xls') or lower_name.endswith('.xlsx') or lower_name.endswith('.xlsb')): continue print(f'Converting file: {file_name}') file_path = join(myFolder, file_name) # 上下文管理器自动管理文件资源 if lower_name.endswith('.xlsb'): xls_file = pd.ExcelFile(file_path, engine='pyxlsb') else: xls_file = pd.ExcelFile(file_path) with xls_file: for mySheetName in xls_file.sheet_names: df = pd.read_excel(xls_file, sheet_name=mySheetName) df = df.applymap(removeEspecialCharacters) # 用splitext安全提取无后缀文件名,避免文件名含后缀关键字时被错误截断 base_name = splitext(file_name)[0] csvFileName = join(myFolder, f'{base_name}_{mySheetName}.csv') df.to_csv( csvFileName, encoding='utf-8-sig', index=False, sep=",", quoting=csv.QUOTE_NONNUMERIC, quotechar="\"", escapechar="\"", decimal=".", date_format='%Y-%m-%d' ) # 处理完单个文件后强制回收资源,即时释放文件锁 gc.collect()
补充说明:如果使用上述代码后仍存在锁定问题,可以升级对应的Excel读取引擎到最新版本,执行
pip install --upgrade openpyxl xlrd pyxlsb pandas即可修复旧版本的已知句柄泄漏bug。
内容的提问来源于stack exchange,提问作者Paulo Coelho
相关产品推荐
相关产品推荐

