尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Python批量重命名文件实战:从基础方法到Excel对照改名

Python批量重命名文件实战:从基础方法到Excel对照改名 之前遇到一次项目文件整理几万个设备照片、合同扫描件混在同一个文件夹里手动重命名按一天处理 500 个算也要忙一个多月。后来改用 Python 写脚本批量处理从“读取文件列表”到“按规则改名”只花了几十秒而且不会漏、不会错、不会重复。这篇教程就把这套批量重命名方案完整拆解从环境准备到代码实现再到几万个文件场景下的性能优化和排错清单全部整理出来适合需要经常整理文件的测试、运维、数据处理同学也适合刚学 Python 的读者照着练习。1. 文件批量重命名为什么需要脚本而不是手工操作1.1 手工重命名的真实痛点文件重命名看起来是一件特别“轻量”的事情。少量文件时右键单击、选择重命名、输入新名称确实很直接。但文件数量上升到几千、几万时手工操作会出现几个现实问题。第一是效率极低。一般人对文件改名一次操作耗时大约 3 到 5 秒1 万个文件就需要 8 到 14 个小时连续工作期间还不能休息。即使能忍耐这个时间成本大量重复劳动也容易让人疲劳。第二是准确性难保证。手工输入文件名时很容易出现错别字、多一个空格、少一个下划线这类问题。文件名本身没有统一规律时后续查找、归档、导入系统都会受影响。第三是难以按照规则批量生成新名称。比如批量给文件加上日期前缀、把中文命名转成拼音、按某一份 Excel 里的对照表逐条改名这些需求手工根本做不了只能靠脚本或专业工具完成。1.2 操作系统自带重命名功能的局限Windows 系统自带的全选重命名确实能解决一部分编号类需求。选中多个文件后按 F2输入一个名称系统会自动生成“名称 (1)”“名称 (2)”“名称 (3)”这种序列。但它的规则非常固定不能自定义补零位数不能改变文件顺序也不能根据文件内容或外部表格生成名字。Linux 系统可以使用 rename 命令或 for 循环处理功能更强但 Shell 脚本处理含空格、中文、特殊字符的文件名时需要额外转义对很多开发者来说并不直观。而且如果以后想在 Windows 环境中复用同一套逻辑Shell 脚本需要重写。Python 脚本则可以跨平台运行同一套批量重命名逻辑在 Windows、Linux、macOS 上都能跑只需要注意路径分隔符差异。更重要的是Python 可以组合“文件名解析、正则匹配、Excel 数据读取、异常记录”这些能力真正做到可定制、可复用、可试运行。1.3 Python 批量重命名适合解决哪些问题从实际使用场景来看Python 批量重命名能覆盖以下几类典型需求。统一规范化把文件名中的空格替换为下划线把中文括号统一成英文括号删除文件名的乱码字符。批量添加前缀或后缀按时间、项目编号、部门名称给文件批量增加固定标识。按规则重新编码将原来的无规则编号改成“日期 序号”格式例如 20250601_001.pdf。对照文件批量改名一份 Excel 表格里写明“原文件名 → 新文件名”脚本读取后逐条执行。过滤式整理的配套动作先找出不符合规则的文件再统一自动重命名。这类需求在实施数据归档、影像资料整理、合同文件归档、测试报告归档时经常出现。掌握一个可扩展的 Python 脚本就相当于把文件重命名这件事标准化了。2. 环境准备Windows 下安装与验证 Python2.1 先确认电脑是否已经安装 Python在 Windows 下打开命令提示符Win R输入 cmd 后回车执行下面的命令python --version如果系统输出了类似 Python 3.10.11 的版本信息说明 Python 已经安装。如果提示“python 不是内部或外部命令”说明还没有安装或者安装时没有勾选“Add Python to PATH”。也可以检查 py 启动器py --versionWindows 系统中微软商店版 Python 和官网安装版 Python 可能同时存在建议尽量统一到官网安装版本方便管理 pip 和第三方库。2.2 官网安装 Python并勾选关键选项到 Python 官网下载对应系统的安装包。安装过程中有两个地方需要特别注意。第一第一个界面最下方的“Add Python to PATH”复选框一定要勾上。如果没有勾选安装完成后命令行里无法直接执行 python需要手动配置环境变量新手很容易卡在这一步。第二建议选择“Customize installation”进入自定义界面把 pip 组件一并安装。pip 是后续安装第三方库的基础工具。默认全选也没有问题。安装完成后重新打开一个命令提示符窗口再次执行python --version pip --version如果两行命令都正常输出说明 Python 和 pip 环境已经可以正常使用。本文示例基于 Python 3 的常见版本编写如果你本机已经是 Python 3.6 以上的版本核心代码逻辑均可直接运行。2.3 准备测试目录与实验文件在开始写批量重命名脚本之前强烈建议先建立一个独立的测试目录避免在重要文件夹里直接运行脚本导致不可逆影响。例如在 D 盘建立一个 TestFiles 目录mkdir D:\TestFiles然后手动创建 10 个左右的测试文件可以是一批 txt 文件也可以是一批任意文件。如果文件数量少直接右键新建即可。接下来所有示例脚本都会优先针对这个测试目录运行确认结果正确后再替换成真实文件路径。测试阶段还可以把文件复制一份作为备份确保改错时有恢复手段。真实生产文件在批量重命名前同样应该预留备份或事先导出一份文件清单。3. Python 批量重命名的核心方法与实现原理解析3.1 os.rename重命名文件的基础方法Python 对文件重命名最基础的方法是 os.rename。它的作用是把一个已存在的文件路径修改为另一个新路径。调用需要两个参数第一个是原路径第二个是新路径。import os # 文件路径demo_os_rename.py old_name rD:\TestFiles\old_demo.txt new_name rD:\TestFiles\new_demo.txt # 将 old_demo.txt 重命名为 new_demo.txt os.rename(old_name, new_name) print(重命名完成)这段代码运行后D:\TestFiles 目录下原来的 old_demo.txt 会消失取而代之的是 new_demo.txt。需要特别注意的是os.rename 并不关心路径指向的是普通文件还是目录它只做“把原路径改为新路径”这一件事。在实际批量重命名脚本中建议先用 os.path.isfile 或其他方法判断目标路径确实是文件避免把文件夹整个改名。3.2 os.replace更安全的跨平台替代方案在 Windows 平台上如果新路径已经存在同名文件os.rename 会直接抛出 FileExistsError。这种场景在实际整理文件时很容易出现比如两个不同目录下的文件改名后落到同一个目标名字。os.replace 与 os.rename 的功能类似但它的行为是如果新路径已存在文件则覆盖旧文件。这个特性在部分场景下更合适但也更危险因为一旦覆盖原文件内容就没了。import os # 文件路径demo_os_replace.py old_path rD:\TestFiles\source.txt new_path rD:\TestFiles\target.txt # 如果 target.txt 已存在会被直接替换 os.replace(old_path, new_path)真实项目里到底选 os.rename 还是 os.replace取决于业务需求。如果目标是“整理后不允许出现同名覆盖”应该先做重名校验再调用 os.rename如果业务上允许用新文件覆盖旧文件才考虑 os.replace。对新手来说优先使用 os.rename 并在重命名前检查目标是否存在是最稳妥的策略。3.3 pathlib.Path.rename更现代的路径操作方式Python 3.4 之后引入了 pathlib 库处理路径时比 os.path 字符串拼接更直观。pathlib 中每个文件路径都是 Path 对象重命名直接调用 Path.rename 方法。from pathlib import Path # 文件路径demo_pathlib_rename.py base_dir Path(rD:\TestFiles) old_path base_dir / old_demo.txt new_path base_dir / new_demo.txt # 重命名 old_path.rename(new_path) print(重命名完成新文件是否存在, new_path.exists())可以看到Path 对象使用 / 运算符拼接路径不需要自己手写反斜杠也不容易漏掉分隔符。在编写递归遍历文件夹时pathlib 配合 rglob、iterdir 等方法会让代码更简洁建议在 Python 3.6 以上环境优先选择。3.4 获取文件列表os.listdir 与 os.walk 的区别批量重命名的第一步永远是先拿到需要处理的文件列表。Python 里常用的方式是 os.listdir 和 os.walk两者适用场景不一样。os.listdir 只列举指定目录下的一层内容包括文件和子目录。如果只需要处理某个文件夹里的直接子文件它足够用。import os # 文件路径demo_listdir.py folder rD:\TestFiles for name in os.listdir(folder): full_path os.path.join(folder, name) if os.path.isfile(full_path): print(文件, full_path) else: print(子目录, full_path)os.walk 则会递归遍历目录树对于几万个文件分布在不同子目录的情况os.walk 能依次返回当前目录、子目录列表、文件列表适合实现“全目录统一重命名”的需求。import os # 文件路径demo_walk.py root_dir rD:\TestFiles for current_dir, sub_dirs, files in os.walk(root_dir): print(当前目录, current_dir) for file_name in files: print(文件, file_name)使用 os.walk 遍历时要注意一点如果我们在遍历过程中直接对当前目录下文件执行重命名并且重命名后的文件名可能再次匹配到某些规则可能会造成同一文件被二次处理。最稳妥的做法是先收集所有文件完整路径再统一执行重命名。3.5 拆分文件名名称部分与后缀部分批量重命名的核心操作之一是对文件名做拆分。例如要把“2024 报表 final.txt”改成“2024_报表_final.txt”需要把后缀 .txt 先分开处理后缀名前的字符串最后重新拼接后缀。常见做法是使用 os.path.splitext 方法。它会把一个文件名分成“主名”和“后缀”两部分。import os # 文件路径demo_splitext.py filename 2024 报表 final.txt name_part, ext_part os.path.splitext(filename) print(主名部分, name_part) print(后缀部分, ext_part)运行结果主名部分 2024 报表 final 后缀部分 .txt有了主名部分之后就可以对它做替换、正则匹配、切片、补零等操作。处理完成后再把新主名与后缀连接起来得到最终的新文件名。3.6 安全校验防止重命名时出现意外错误直接执行 os.rename 之前建议做几项基础检查能减少大量问题。第一个检查是目标路径是否存在。如果新文件名已经存在需要打印告警并跳过。第二个检查是文件是否有特殊属性。例如只读文件重命名偶尔会失败Windows 下可以先尝试修改文件属性。第三个检查是目录是否可写。权限不足时os.rename 会抛出 PermissionError脚本应该记录异常而不是直接崩溃。下面是一个带检查的简单版本示例import os # 文件路径demo_safe_rename.py folder rD:\TestFiles old_name old_demo.txt new_name new_demo.txt old_path os.path.join(folder, old_name) new_path os.path.join(folder, new_name) if not os.path.exists(old_path): print(原文件不存在请检查路径, old_path) elif os.path.exists(new_path): print(目标文件已存在重命名中止, new_path) else: os.rename(old_path, new_path) print(重命名成功, old_name, -, new_name)这种“先校验、再执行”的思路是生产环境脚本的基础。后面完整实战案例中还会把它升级成批量版本并为每个失败文件生成日志。4. 实战案例一批量清理并规范化文件夹内所有文件名4.1 需求背景与规则设计假设有一个文件夹 D:\ProjectFiles里面存放了大量项目过程文件文件名很不规则例如存在多个空格、中文括号、多余前缀、错误的后缀。现在需要做以下四件事把文件名中的连续多个空格替换成单个下划线。把中文全角括号替换成英文半角括号()。删除文件名开头的“【临时】”前缀。统一将所有小写后缀转换为小写去掉多余空白后缀。在真实场景里这类需求常见于资料归档前。文件数量上百上千时靠手工一个个改根本不现实。为了让效果更明显先准备一个目标文件夹并确认目录内文件如下【临时】项目总结 报告 (终版).TXT 【临时】项目计划 v2 说明.TXT 测试 数据 2024 完整版.TXT 会议 记录 (2024-06-01).txt需要说明的是Windows 默认不区分扩展名大小写但为了保证归档规范脚本里仍会统一处理后缀字符串。4.2 先构造精准的正则替换函数这里新建一个文件命名为 batch_rename_01.py。处理文件名文本时先用 Python 内置的 re 模块实现两个替换逻辑。第一个逻辑是把连续多个空格替换成单个下划线第二个逻辑是把连续多个下划线合并成一个。# -*- coding: utf-8 -*- # 文件路径batch_rename_01.py import os import re from pathlib import Path def normalize_filename(filename: str) - str: 对单个文件名进行规范化清理。 输入参数 filename 是带后缀的完整文件名。 返回值为清理后的文件名如果清理后为空则保留原名。 name_part, ext_part os.path.splitext(filename) # 去除文件名开头可能存在的多余空格 name_part name_part.strip() # 替换全角括号为半角括号 name_part name_part.replace(, ().replace(, )) # 删除“【临时】”前缀 if name_part.startswith(【临时】): name_part name_part[len(【临时】):] # 连续多个空格替换为单下划线 name_part re.sub(r\s, _, name_part) # 连续多个下划线合并为单个下划线 name_part re.sub(r_, _, name_part) # 后缀统一转小写并去除后缀中的空白字符 ext_part ext_part.strip().lower() # 如果主名清理后为空说明原文件名本身有问题直接返回原名字 if not name_part: return filename return name_part ext_part if __name__ __main__: # 方便独立测试 test_names [ 【临时】项目总结 报告 (终版).TXT, 【临时】项目计划 v2 说明.TXT, 测试 数据 2024 完整版.TXT, 会议 记录 (2024-06-01).txt, ] for old in test_names: print(old, , normalize_filename(old))执行一次该函数测试python batch_rename_01.py可以看到输出结果【临时】项目总结 报告 (终版).TXT 项目总结_报告_(终版).txt 【临时】项目计划 v2 说明.TXT 项目计划_v2_说明.txt 测试 数据 2024 完整版.TXT 测试_数据_2024_完整版.txt 会议 记录 (2024-06-01).txt 会议_记录_(2024-06-01).txtnormalize_filename 函数是整个脚本里的核心文本处理模块后续如果需要增加“去除表情符号”“中文转拼音”“标准化日期格式”等规则都可以在这个函数里继续补充。4.3 主流程遍历文件并执行可回滚的重命名下一步在同一个文件里加入主流程。主流程的步骤是指定目标文件夹。遍历文件夹内所有文件。跳过子目录只处理文件。对每个文件调用 normalize_filename。如果新文件名与旧文件名不同先检查目标文件是否存在。记录成功日志与失败日志。# -*- coding: utf-8 -*- # 文件路径batch_rename_01.py续 import os import re from datetime import datetime from pathlib import Path def collect_files(folder: str): 收集文件夹下所有文件的完整路径。 这里只处理一层目录如果需要递归所有子目录 可以把 iterdir 换成 rglob(*)。 base_dir Path(folder) if not base_dir.exists(): print(目录不存在, folder) return [] file_paths [] for item in base_dir.iterdir(): if item.is_file(): file_paths.append(item) return file_paths def rename_all_in_folder(folder: str): 批量规范化文件夹下所有文件的文件名。 执行分两个阶段先计算并打印计划再真正执行。 file_paths collect_files(folder) print(待处理文件数量, len(file_paths)) rename_plan [] for file_path in file_paths: old_name file_path.name new_name normalize_filename(old_name) if new_name ! old_name: rename_plan.append((file_path, old_name, new_name)) print(需要重命名的文件数量, len(rename_plan)) # 打印预览方便人工确认规则是否生效 for file_path, old_name, new_name in rename_plan[:20]: print(准备重命名, old_name, , new_name) # 询问确认避免脚本一运行就造成不可逆修改 confirm input(是否继续执行重命名输入 yes 后回车确认) if confirm.strip().lower() ! yes: print(已取消未对任何文件进行修改) return success_count 0 fail_count 0 log_time datetime.now().strftime(%Y%m%d_%H%M%S) log_file frename_log_{log_time}.txt with open(log_file, w, encodingutf-8) as log_fp: for file_path, old_name, new_name in rename_plan: new_path file_path.with_name(new_name) if new_path.exists(): message f跳过目标文件已存在{file_path} - {new_path} print(message) log_fp.write(message \n) fail_count 1 continue try: file_path.rename(new_path) success_count 1 log_fp.write(f成功{old_name} - {new_name}\n) except Exception as exc: fail_count 1 error_message f失败{old_name} - {new_name}错误{exc} print(error_message) log_fp.write(error_message \n) print(全部处理完成) print(成功数量, success_count) print(失败数量, fail_count) print(日志文件, log_file) if __name__ __main__: # normalize_filename 测试代码此处省略完整代码可合并编译 target_folder rD:\ProjectFiles rename_all_in_folder(target_folder)这里有两个细节值得展开说明。第一个是 new_path.exists() 检查。它防止两个文件被改成同一个名字时互相覆盖这也是批量重命名最容易发生的灾难之一。第二个是日志文件记录。几万文件的批量操作必须在日志里保留“成功/失败”明细否则一旦中间出现异常很难定位哪些文件没有处理。写成日志文件比只输出到控制台更可靠因为控制台输出太长时前面的信息会被滚动冲掉。4.4 执行验证与预期结果运行上面的脚本后如果测试目录内文件正确会先看到类似下面的预览内容待处理文件数量 4 需要重命名的文件数量 4 准备重命名 【临时】项目总结 报告 (终版).TXT 项目总结_报告_(终版).txt 准备重命名 【临时】项目计划 v2 说明.TXT 项目计划_v2_说明.txt 准备重命名 测试 数据 2024 完整版.TXT 测试_数据_2024_完整版.txt 准备重命名 会议 记录 (2024-06-01).txt 会议_记录_(2024-06-01).txt 是否继续执行重命名输入 yes 后回车确认确认输入 yes 后脚本执行重命名并输出成功数量 4 失败数量 0 日志文件 rename_log_20250601_120000.txt此时再用资源管理器查看 D:\ProjectFiles文件已经全部变成规范后的名称。建议所有首次尝试的朋友都先找一个小文件目录演练不要直接在重要目录里运行完整脚本。脚本无误后再扩展到大目录。5. 实战案例二根据 Excel 对照表批量重命名 Word 文件5.1 场景描述某公司每个季度会收到渠道方发来的几百份 Word 合同文件文件名是“合同_编号_姓名.docx”但内部系统要求文件名称必须是“签约日期_客户名称.docx”。有一份 Excel 表已经维护好了“旧文件名 → 新文件名”的对应关系例如原文件名新文件名合同_编号_张三.docx20250601_张三.docx合同_编号_李四.docx20250602_李四.docx合同_编号_王五.docx20250603_王五.docx现在需要根据这张 Excel 表批量重命名真实的 Word 文件。这也是“如何根据 excel 表格批量重命名对应的 word 文件名称”这类热搜问题的核心诉求。注意一个关键点Windows 资源管理器显示文件名时默认不显示扩展名。如果 Excel 表里的“原文件名”列没有写 .docx但磁盘上真实文件叫“合同_编号_张三.docx”脚本需要能够自动补全扩展名这样才能准确匹配文件。5.2 安装 openpyxl 并读取 Excel读取 .xlsx 文件最常用的 Python 库是 openpyxl。如果没有安装需要先执行pip install openpyxl安装完成后在脚本里使用 Excel 表时可以用 load_workbook 加载工作簿再通过 sheet 名称或索引读取单元格内容。下面先编写一个只负责读取 Excel 对照表的函数# -*- coding: utf-8 -*- # 文件路径read_excel_map.py import os from openpyxl import load_workbook def read_rename_map(excel_path: str, sheet_name: str None): 读取 Excel 中的原文件名与新文件名对照关系。 默认读取第一个 sheet且表头为 第一列原文件名 第二列新文件名 返回一个字典{原文件名: 新文件名} wb load_workbook(excel_path, data_onlyTrue) if sheet_name: ws wb[sheet_name] else: ws wb.active rename_map {} for row in ws.iter_rows(min_row2, values_onlyTrue): if row is None or len(row) 2: continue old_name str(row[0]).strip() new_name str(row[1]).strip() if not old_name or not new_name or old_name None: continue rename_map[old_name] new_name wb.close() return rename_map if __name__ __main__: # 修改为实际 Excel 路径 result read_rename_map(rD:\ProjectFiles\rename_map.xlsx) for old, new in result.items(): print(old, , new)这里使用了 data_onlyTrue表示读取单元格中显示的值。如果 Excel 里的新文件名是通过函数公式生成的这个参数能拿到公式计算后的结果。如果公式没有计算并缓存可能会读到 None因此更稳妥的实践是在 Excel 中事先粘贴成数值。5.3 根据扩展名自动匹配磁盘文件从 Excel 表里读取到的原文件名可能没有扩展名真实文件则带有扩展名。最简单有效的策略是先读取目标目录下的所有文件构建“不带后缀的文件名 → 完整文件名”的映射。# -*- coding: utf-8 -*- # 文件路径build_file_index.py from pathlib import Path def build_file_index(folder: str): 构建目录下所有文件映射。 返回两个字典 1. name_only_map: 不带后缀的文件名 - Path 2. full_name_map: 带后缀的完整文件名 - Path base_dir Path(folder) name_only_map {} full_name_map {} if not base_dir.exists(): return name_only_map, full_name_map for file_path in base_dir.iterdir(): if file_path.is_file(): full_name_map[file_path.name] file_path name_only_map[file_path.stem] file_path return name_only_map, full_name_map这样设计的好处是无论 Excel 表里写的是完整文件名还是不带扩展名的名字都能在磁盘文件里快速定位目标。面对几万个文件时使用字典而不是一层 for 循环反复遍历能避免很多不必要的性能损耗。如果采用两层 for 循环逐条比对5 万个文件与 5 万行 Excel 记录就是 25 亿次比较Python 运行起来会非常慢。字典的查找复杂度接近 O(1)几十万级别的文件量也能在极短时间内处理。5.4 主流程逐条执行并记录未匹配项根据 Excel 表批量重命名的完整逻辑可以合并成 batch_rename_by_excel.py# -*- coding: utf-8 -*- # 文件路径batch_rename_by_excel.py import sys from datetime import datetime from pathlib import Path from openpyxl import load_workbook def read_rename_map(excel_path: str): 读取 Excel 前两列作为映射关系 wb load_workbook(excel_path, data_onlyTrue) ws wb.active rename_map {} for row in ws.iter_rows(min_row2, values_onlyTrue): if row is None or len(row) 2: continue old_name str(row[0]).strip() new_name str(row[1]).strip() if old_name None or new_name None or not old_name or not new_name: continue rename_map[old_name] new_name wb.close() return rename_map def build_file_index(folder: str): 构建目录下的文件索引 base_dir Path(folder) name_only_map {} full_name_map {} if base_dir.exists(): for file_path in base_dir.iterdir(): if file_path.is_file(): full_name_map[file_path.name] file_path name_only_map[file_path.stem] file_path return name_only_map, full_name_map def run_batch_rename(excel_path: str, file_folder: str): rename_map read_rename_map(excel_path) name_only_map, full_name_map build_file_index(file_folder) print(Excel 映射条数, len(rename_map)) print(目录内文件数量, len(full_name_map)) success_count 0 skip_count 0 fail_count 0 not_found_list [] log_time datetime.now().strftime(%Y%m%d_%H%M%S) log_file fexcel_rename_log_{log_time}.txt with open(log_file, w, encodingutf-8) as log_fp: for excel_old, excel_new in rename_map.items(): # 先尝试使用完整文件名匹配 file_path full_name_map.get(excel_old) # 如果完整文件名没匹配到则尝试不带后缀匹配 if file_path is None: file_path name_only_map.get(excel_old) if file_path is None: not_found_list.append(excel_old) message f未找到文件{excel_old} print(message) log_fp.write(message \n) fail_count 1 continue old_name file_path.name # 如果新文件名没有带原后缀自动补全 new_full_name excel_new if not Path(excel_new).suffix: new_full_name excel_new file_path.suffix new_path file_path.with_name(new_full_name) if new_path.exists(): message f目标已存在跳过{old_name} - {new_full_name} print(message) log_fp.write(message \n) skip_count 1 continue try: file_path.rename(new_path) message f成功{old_name} - {new_full_name} print(message) log_fp.write(message \n) success_count 1 except Exception as exc: fail_count 1 message f失败{old_name} - {new_full_name}错误{exc} print(message) log_fp.write(message \n) print(批量重命名完成) print(成功数量, success_count) print(跳过数量, skip_count) print(失败数量, fail_count) if not_found_list: print(未匹配到磁盘文件的 Excel 记录数量, len(not_found_list)) if __name__ __main__: excel_file rD:\ProjectFiles\rename_map.xlsx file_dir rD:\ProjectFiles\WordFiles run_batch_rename(excel_file, file_dir)这段脚本的核心逻辑是先用“完整文件名”匹配再用“不带后缀的文件名”匹配。两种方式都找不到时把记录加进 not_found_list便于事后检查是不是 Excel 数据写错或文件确实缺失而不是静默跳过。补全后缀的逻辑也值得留意如果 Excel 新文件名没有写“.docx”脚本会使用原文件的后缀自动补上避免生成没有扩展名的文件。5.5 处理结果与日志分析如果目录里有 300 个待改名文件Excel 里有 300 条映射脚本执行完成后会统计成功数量 298 跳过数量 0 失败数量 2 未匹配到磁盘文件的 Excel 记录数量 2这时可以根据日志文件里的“未找到文件”信息逐一排查。常见原因包括Excel 表里的原文件名与实际文件名不一致。真实文件在子目录中而脚本只扫描了一层目录。原文件名包含不可见字符或复制粘贴时多了空格。处理这类问题时可以先打印文件名的 repr 表示来观察隐藏字符。例如在 Python 中执行python -c from pathlib import Path; print([repr(p.name) for p in Path(rD:\ProjectFiles\WordFiles).iterdir()])这时如果看到类似 合同_编号_张三.docx 这样的输出说明文件名末尾多了一个空格肉眼查看时几乎看不出来。Excel 表里没有这个空格所以匹配失败。解决方案是在脚本中对文件名执行 strip 操作或统一在 Excel 数据整理阶段清洗空白字符。6. 几万个文件场景下的性能优化与脚本改进6.1 文件数量大时主要耗时在哪里几万个文件的批量重命名真正耗时的往往不是重命名本身而是文件系统操作、文本处理、路径拼接以及日志输出。os.rename 一次只执行一个系统调用单个文件虽然很快但几万个文件累积起来文件系统 I/O 会成为主要瓶颈。另一个容易忽略的问题是遍历目录的方式。如果目录里有很多层子目录并且文件分布在各个层级使用 os.walk 或 Path.rglob 都需要逐层扫描目录树。为了拿到完整文件列表这一过程本身就需要时间但通常可以接受。脚本里频繁打印每一条记录到控制台也会明显降低处理速度。处理几百个文件时差异不大几万个文件时控制台刷新会产生大量额外开销。生产脚本建议只在出错时打印信息成功日志写入文件。6.2 使用 os.scandir 提升遍历性能如果使用 os.listdir 遍历超大目录它会一次性返回目录下全部名称列表使用 os.scandir 则可以在迭代过程中逐步获取文件信息同时能直接拿到文件类型减少一次 stat 系统调用。import os # 文件路径demo_scandir.py folder rD:\ProjectFiles file_count 0 with os.scandir(folder) as entries: for entry in entries: if entry.is_file(): file_count 1 # entry.name 是文件名 # entry.path 是完整路径 pass print(文件数量, file_count)如果文件数量达到几万甚至更多使用 os.scandir 比 os.listdir 更合适。对于“只需要第一层文件”的场景可以将前面 collect_files 函数中的 iterdir 改成 os.scandir 写法。6.3 批量执行前使用演练模式实际项目中最怕的不是脚本慢而是规则设计有误导致大量文件被改成错误名称。为此可以在脚本中加入演练模式也就是只计算新文件名并打印预览不真正调用 rename。核心思路是增加一个参数 dry_run当它为 True 时只输出准备执行的操作为 False 时才真正写入文件。# 文件路径dry_run_demo.py from pathlib import Path def dry_run_rename(folder: str, dry_run: bool True): 演练模式示例 dry_runTrue 时仅打印将要执行的操作。 base_dir Path(folder) plan [] for file_path in base_dir.iterdir(): if not file_path.is_file(): continue new_name prefix_ file_path.name plan.append((file_path, file_path.with_name(new_name))) print(本次将要重命名 {} 个文件.format(len(plan))) if dry_run: for old_path, new_path in plan[:10]: print([演练] {} - {}.format(old_path.name, new_path.name)) return for old_path, new_path in plan: old_path.rename(new_path) print(重命名完成) if __name__ __main__: # 先演练再执行 dry_run_rename(rD:\ProjectFiles\Demo, dry_runTrue)演练模式看似简单却能避免很多误操作。强烈建议在正式脚本中添加该参数无论是处理几千个文件还是几万个文件先跑一次演练确认格式无误再真正执行。6.4 并行处理在文件重命名场景是否有必要有人会想到多线程或进程池加速文件重命名。实际上对于普通的磁盘文件系统文件重命名的瓶颈主要在磁盘 I/O 和文件系统元数据更新盲目增加线程并不能带来数量级的性能提升甚至可能因为并发访问同一目录导致额外冲突。因此在批处理重命名中默认不要使用多线程。脚本逻辑保持简单、可预测、可重入远比追求微小的速度提升重要。只有在网络文件系统、对象存储这类特殊场景才需要考虑并发策略而那通常也不是用 os.rename 能解决的问题。6.5 防止重复执行造成二次改名脚本在一次运行中处理完所有文件后如果因为中途报错或日志没看明白开发者可能会再次运行脚本。此时如果新文件名又满足某条重命名规则文件会被再次改名最终结果很可能不符合预期。解决思路是在脚本开头维护一个已处理标记。常见的做法是在文件名中添加固定前缀虽然这改变了文件内容无关的命名更好的做法是每次先记录一份“原文件列表”并把该列表保存成清单文件重跑前人工确认清单。另一个思路是让脚本具备“根据已有命名规则反向判断文件是否已处理”的能力。例如统一重命名后的文件都以“IMG_”开头处理前先跳过所有符合目标格式的文件。这样能让脚本具备幂等性规避重复执行问题。7. Python 批量重命名常见问题与排查清单7.1 常见错误现象与处理方法问题现象常见原因解决思路FileNotFoundError原文件路径不存在或文件名里有不可见字符打印路径的 repr检查前后空格PermissionError文件被 Word/WPS 占用或没有写入权限关闭文件后重试检查目录权限FileExistsError目标文件名已存在重命名前检查目标是否存在UnicodeEncodeError文件名包含系统不支持的特殊字符使用 errors 参数或打印文件名时替换异常字符Excel 中读取到 None单元格为空或公式没有缓存值使用 data_onlyTrue检查 Excel 单元格目标目录找不到文件文件在子目录中改用 os.walk 或 Path.rglob 递归遍历重命名后文件后缀消失新文件名没有补全后缀使用 Path(excel_new).suffix 判断并补全中文文件名乱码终端编码或 locale 问题设置 PYTHONIOENCODINGutf-8Linux 下使用 Shell 批量重命名时还可能遇到文件名包含换行符、空格等特殊情况。Python 脚本在处理这类文件名时天然更有优势因为文件名在 Python 中就是普通字符串不需要额外转义。7.2 Python 文件重命名常见报错排查清单实际排错时建议按照以下顺序逐步确认第一先检查路径。是否为绝对路径目录是否存在路径分隔符是否正确建议全部使用 Path 对象避免手动拼接字符串出错。第二检查文件名。是否存在特殊字符Windows 下文件名不能包含 \ / : * ? |如果新文件名由 Excel 内容拼接而来很可能会混入这些非法字符。第三检查文件占用。Windows 下如果文件正在被 Office、阅读器打开重命名会抛出 PermissionError。脚本执行前应关闭相关程序或加入异常捕获并在异常信息中提示用户关闭占用程序。第四查看日志。日志里记录了文件从旧名称到新名称的完整过程。如果发现某一条失败根据具体异常类型快速定位。第五恢复原样。如果已经执行了一次有问题的重命名可以通过另一份“反向映射”脚本还原。所以在执行前保留一个 filelist.txt 清单永远是明智的选择。7.3 Windows 终端粘贴路径时的格式问题Windows 终端中复制文件夹路径后通常是“D:\ProjectFiles”这种形式。在 Python 字符串中直接写这种路径反斜杠会被解释成转义符。例如 \P 不是标准转义序列Python 会保留反斜杠但 \n、\t 这类组合会被错误处理。推荐三种稳妥写法# 写法1使用原始字符串 folder rD:\ProjectFiles # 写法2使用正斜杠 folder D:/ProjectFiles # 写法3使用 Path 拼接 from pathlib import Path folder Path(D:/ProjectFiles)在示例脚本中都优先使用了 pathlib这能减少路径格式带来的大多数问题。8. 批量重命名工程化的最佳实践与扩展思路8.1 执行前先备份文件清单而不是复制全部文件复制几万个文件作为备份耗时又占空间。在处理文件重命名时更推荐先备份文件清单也就是把所有文件的完整路径导出一份列表。# 文件路径export_file_list.py from pathlib import Path def export_file_list(folder: str, output: str): base_dir Path(folder) with open(output, w, encodingutf-8) as fp: for file_path in base_dir.rglob(*): if file_path.is_file(): fp.write(str(file_path) \n) if __name__ __main__: export_file_list(rD:\ProjectFiles, file_list_backup.txt)这样做的意义在于如果重命名规则设计错误可以根据这份清单精确判断哪些文件被改过、原本叫什么名字然后编写还原脚本恢复。相比备份全部文件成本和风险都更低。8.2 新文件名由外部数据生成时必须做合法性过滤当新文件名来自 Excel 或数据库时不能直接信任数据内容。Windows 和 Linux 对文件名字符集的限制不同。建议在脚本中增加一个名称清理函数过滤掉非法字符。import re # 文件路径sanitize_filename.py INVALID_CHARS r[\\/:*?|] def sanitize_filename(name: str) - str: 删除 Windows 下文件名不允许出现的字符。 同时做简单的空格清理避免末尾出现点或空格。 name re.sub(INVALID_CHARS, , name) name name.strip().strip(.) return name如果业务系统对文件名长度有限制还要控制最长长度。Windows 完整路径通常限制在 260 个字符左右虽然新版 Windows 可以通过注册表开启长路径支持但并不建议把文件名设计得过长。8.3 使用统一日志记录每次文件操作无论是 os.rename 成功还是失败日志都应记录以下信息操作时间。原文件完整路径。新文件完整路径。操作结果成功/失败/跳过。失败原因。日志文件本身建议保存在脚本所在目录之外的独立日志文件夹文件名带时间戳方便追溯。通过日志还能统计成功数量、失败数量、跳过数量用于验证整个重命名过程的完整性。8.4 从脚本到工具将 Python 文件打包成 exe在很多办公场景里编写脚本的人往往不是最终执行者。如果要把脚本分发给不懂 Python 的同事使用可以用 PyInstaller 将脚本打包成 exe 文件。安装 PyInstallerpip install pyinstaller打包命令pyinstaller -F -w batch_rename_gui.py其中 -F 表示生成单个 exe 文件-w 表示运行时不显示控制台窗口。如果使用 -w需要脚本中提供 GUI 界面否则用户看不到任何操作提示。实际上更快捷的方式是不使用 -w保留控制台窗口让用户按提示输入文件夹路径。但 PyInstaller 打包后的 exe 容易被杀毒软件误报这是 Windows 下常见现象。建议将代码保存为 .py 文件并提供一份“双击运行.bat”批处理脚本让相关同事在装有 Python 的电脑上直接执行。这种方式比分发 exe 更容易维护和更新。8.5 可扩展命名规则的维护思路批量重命名脚本的文本处理部分应该根据实际情况持续演进。很多场景不是一次性整理而是周期性执行的例如每月归档文件时都需要自动重命名。建议把“核心重命名规则”独立到一个规则函数中并将规则配置化。例如本月规则是在文件名前加月份前缀下月可能变成在文件名中插入合同号。将这些规则抽成参数后脚本主体就不需要频繁改动。# 文件路径rule_engine_demo.py def generate_new_name(old_name: str, prefix: str , suffix: str ) - str: 可配置的前后缀插入函数 name_part, ext_part os.path.splitext(old_name) return f{prefix}{name_part}{suffix}{ext_part}这样不仅方便维护也降低了出错的概率。9. 总结从“手动改名”走向“脚本化管理文件”这篇文章围绕 Python 批量重命名文件做了系统拆解。先是说明为什么大量文件不适合手工处理然后介绍了 os.rename、os.replace、pathlib 的重命名用法再结合两个完整实战案例演示了普通规范化批量改名和根据 Excel 对照表批量改名。对几万个文件场景下的目录遍历、演练模式、日志记录、异常处理也给出了明确建议。以后遇到大量文件需要统一改名时可以按下面这套思路来执行第一明确需求文件名要变成什么规则是简单的加前缀还是要从 Excel 读取映射关系第二准备测试目录先用几十个测试文件运行脚本确认逻辑无误。第三导出原文件清单将所有文件完整路径保存到文本文件便于追溯和恢复。第四开启演练模式先预览将要改名的文件列表不真正执行。第五正式执行并保存日志记录成功、失败、跳过的所有文件方便后续核对。最后提醒一句文件重命名属于不可逆操作即使脚本写得再完善正式执行前也要养成备份文件名清单和试运行的习惯。只要做到这一点即使一次处理几万个文件也不会手忙脚乱。
返回列表