第2章 2.5 综合实战:文件批量重命名工具
🎯 开场:为什么你需要一个批量重命名工具?
上一章我们学会了用 events 事件触发器来监听文件变化——听起来挺酷对吧?但光看不练,很快就会忘。
今天我们来做一个真的能用的小工具:文件批量重命名工具。
你有没有遇到过这些情况?
- 下载了100张照片,名字全是
DSC_0001.jpg、DSC_0002.jpg... 想改成旅行照_2024_01.jpg这种有意义的名字 - 整理文件夹时,发现一堆文件名前面多了
副本 -或者_final_v3这种垃圾字符 - 想给所有文件前面加个日期,或者统一改成「001、002、003」编号
一个个改?手都要废掉。写个脚本?太复杂了。
今天学完,你就能用 50 行代码 写一个你自己的批量重命名工具,而且还能加事件监听——每重命名一个文件,就给你播报一声「好了!」。
🧱 基础:核心概念扫盲(5分钟)
什么是文件批量重命名?
生活类比:想象你有一抽屉的文件夹,上面写的都是「文件夹1」「文件夹2」……你想给它们改成「合同」「发票」「保单」这样有意义的名称。一个一个改太慢,但你有一张「改名前后对照表」,按表批量改,瞬间搞定。
技术本质:就是 遍历文件列表 → 按规则生成新名字 → 重命名。核心就是三步走。
pathlib:Python 里操作文件路径的神器
Node.js 里有 path 模块,Python 里有 pathlib(更现代化)。
from pathlib import Path
# 创建一个路径对象
p = Path("./photos")
# 遍历所有文件
for file in p.glob("*.jpg"):
print(file.name) # 打印文件名(含后缀)
print(file.stem) # 打印文件名(不含后缀)
print(file.suffix) # 打印后缀名(.jpg)

什么是文件事件监听?
生活类比:你订了一份外卖,你不需要一直盯着手机看外卖到哪了——外卖到了骑手会主动打电话通知你。事件监听就是这个道理:不用一直问「好了没」,好了它自然会告诉你。
上一章 events 就是干这个的。我们今天给重命名工具加上事件播报,每重命名完一个文件就触发一个「完成」事件。
🔥 实战:3 个递进项目
项目 1:最最简单的批量重命名(5分钟)
目标:把 photos 文件夹里所有 .jpg 文件改成 照片_001.jpg 这种格式。
from pathlib import Path
def batch_rename():
folder = Path("./photos")
files = sorted(folder.glob("*.jpg")) # 获取所有 jpg 文件
for index, file in enumerate(files, start=1):
# 生成新名字:照片_001.jpg
new_name = f"照片_{index:03d}.jpg" # :03d = 保留3位,不够前面补0
new_path = folder / new_name
# 重命名
file.rename(new_path)
print(f"✅ {file.name} → {new_name}")
batch_rename()
预期输出:
✅ DSC_0001.jpg → 照片_001.jpg
✅ DSC_0002.jpg → 照片_002.jpg
✅ DSC_0003.jpg → 照片_003.jpg
说白了:就是遍历文件列表,按「前缀_序号.后缀」的规则重新起名。
项目 2:读取配置文件,按规则批量重命名(15分钟)
真实场景:你想根据一个 CSV 文件里的映射关系来重命名。比如 CSV 长这样:
原文件名,新文件名
DSC_0001.jpg,旅行照_北京.jpg
DSC_0002.jpg,旅行照_上海.jpg
import csv
from pathlib import Path
def batch_rename_from_csv(csv_path):
folder = Path("./photos")
# 读取 CSV 配置
with open(csv_path, "r", encoding="utf-8") as f:
reader = csv.DictReader(f)
mappings = list(reader)
success_count = 0
for mapping in mappings:
old_name = mapping["原文件名"].strip()
new_name = mapping["新文件名"].strip()
old_path = folder / old_name
new_path = folder / new_name
if old_path.exists():
old_path.rename(new_path)
print(f"✅ {old_name} → {new_name}")
success_count += 1
else:
print(f"⚠️ 文件不存在:{old_name}")
print(f"\n完成!共重命名 {success_count} 个文件")
# 使用
batch_rename_from_csv("./rename_config.csv")
预期输出:
✅ DSC_0001.jpg → 旅行照_北京.jpg
✅ DSC_0002.jpg → 旅行照_上海.jpg
⚠️ 文件不存在:DSC_0099.jpg
完成!共重命名 2 个文件
说白了:CSV 就是一张「改名前后对照表」,程序读进来,按表一个个改。
项目 3:带事件通知的批量重命名工具(15分钟)
目标:做一个真正的 CLI 工具,带进度提示和事件通知。
import csv
from pathlib import Path
from dataclasses import dataclass
from typing import Callable, List
@dataclass
class RenameEvent:
old_name: str
new_name: str
status: str # "success" or "error"
class BatchRenamer:
def __init__(self, folder_path: str):
self.folder = Path(folder_path)
self.listeners: List[Callable] = []
# 注册监听器(类似 Node.js 的 on())
def on(self, event_name: str, callback: Callable):
if event_name == "rename":
self.listeners.append(callback)
# 触发事件(类似 Node.js 的 emit())
def emit(self, event: RenameEvent):
for listener in self.listeners:
listener(event)
def rename_file(self, old_name: str, new_name: str):
old_path = self.folder / old_name
new_path = self.folder / new_name
try:
if not old_path.exists():
raise FileNotFoundError(f"文件不存在:{old_name}")
old_path.rename(new_path)
self.emit(RenameEvent(old_name, new_name, "success"))
except Exception as e:
self.emit(RenameEvent(old_name, new_name, f"error: {e}"))
def run(self, csv_path: str):
print(f"📂 开始批量重命名,当前目录:{self.folder}")
print("-" * 40)
with open(csv_path, "r", encoding="utf-8") as f:
reader = csv.DictReader(f)
mappings = list(reader)
total = len(mappings)
for index, mapping in enumerate(mappings, 1):
old_name = mapping["原文件名"].strip()
new_name = mapping["新文件名"].strip()
self.rename_file(old_name, new_name)
print(f"进度:{index}/{total}")
print("-" * 40)
print("🎉 全部完成!")
# ===== 使用示例 =====
def my_listener(event: RenameEvent):
if event.status == "success":
print(f"🔔 事件触发:{event.old_name} 已改名为 {event.new_name}")
else:
print(f"❌ 事件触发:{event.old_name} 出错了 - {event.status}")
# 创建重命名器
renamer = BatchRenamer("./photos")
renamer.on("rename", my_listener) # 注册监听器
renamer.run("./rename_config.csv")
预期输出:
📂 开始批量重命名,当前目录:/Users/xiaoming/photos
----------------------------------------
🔔 事件触发:DSC_0001.jpg 已改名为 旅行照_北京.jpg
进度:1/3
🔔 事件触发:DSC_0002.jpg 已改名为 旅行照_上海.jpg
进度:2/3
❌ 事件触发:DSC_0099.jpg 出错了 - 文件不存在:DSC_0099.jpg
进度:3/3
----------------------------------------
🎉 全部完成!

说白了:就是用观察者模式给重命名过程加了「广播通知」,每重命名一个就通知所有订阅者。
💪 进阶:常见坑 + 性能小贴士
❌ 坑 1:中文编码问题
Windows 系统默认编码不是 UTF-8,直接读写中文文件名可能报错。
# ❌ 错误写法
with open("rename_config.csv", "r") as f:
...
# ✅ 正确写法
with open("rename_config.csv", "r", encoding="utf-8") as f:
...
❌ 坑 2:重命名目标文件已存在
覆盖了重要文件,哭都来不及。
# ❌ 危险写法
new_path.rename(old_path) # 直接覆盖
# ✅ 安全写法
if new_path.exists():
print(f"⚠️ 目标文件已存在,跳过:{new_path}")
else:
old_path.rename(new_path)
❌ 坑 3:相对路径踩的坑
# ❌ 可能出问题的写法
folder = Path("./photos") # 相对路径可能对不上
# ✅ 稳妥写法
folder = Path(__file__).parent / "photos" # 相对于脚本所在目录
❌ 坑 4:文件被占用时 rename 会失败
# ✅ 加个重试机制
import time
def safe_rename(old_path, new_path, retries=3):
for i in range(retries):
try:
old_path.rename(new_path)
return True
except PermissionError:
if i < retries - 1:
time.sleep(0.5)
else:
raise
return False
💡 调试技巧:print 大法
# 在关键步骤加上 print,打印中间状态
def rename_file(self, old_name, new_name):
print(f"[DEBUG] 准备重命名:{old_name} → {new_name}") # 加这行
old_path = self.folder / old_name
new_path = self.folder / new_name
print(f"[DEBUG] 实际路径:{old_path} → {new_path}") # 加这行
# ...
✏️ 练习题
练习 1(2分钟):改个前缀
- 输入:文件夹里有
a.jpg, b.jpg, c.jpg - 预期输出:改成
照片_a.jpg, 照片_b.jpg, 照片_c.jpg - 提示:只需要改项目 1 里生成新名字的那一行
练习 2(3分钟):加个条件判断
- 输入:在练习 1 基础上,只改以
a开头的文件 - 预期输出:只有
a.jpg改成照片_a.jpg,其他不动 - 提示:加一个
if判断file.stem.startswith("a")
练习 3(5分钟):处理新数据格式
- 输入:有一个 JSON 文件
config.json,内容是{"mappings": [{"from": "旧.txt", "to": "新.txt"}]} - 预期输出:按 JSON 里的映射关系重命名
- 提示:用
json.load()读取,其他逻辑和项目 2 一样
练习 4(5分钟):串起两个项目
- 输入:用项目 2 的 CSV 读取 + 项目 3 的事件机制
- 预期输出:每重命名一个文件,console 输出
[通知] xxx 已处理 - 提示:注册一个 listener,callback 里打印
[通知] {event.new_name} 已处理
练习 5(5分钟):报错分析
- 输入:用户运行脚本后报错
FileNotFoundError: [Errno 2] No such file or directory: 'photo.jpg' - 预期输出:说出 3 个可能的原因
- 提示:考虑文件名拼写、大小写、路径问题
作业:做一个带 GUI 提示的批量重命名工具
需求描述:做一个更实用的命令行工具,可以处理多种重命名规则。
功能点:
1. 支持 3 种模式:序号模式(001、002)、日期模式(自动从文件属性读)、前缀模式(用户指定前缀)
2. 带dry-run模式(只显示预览,不实际改名)
3. 支持 -v 参数显示详细信息
加分项:
1. 用 argparse 做命令行参数解析
2. 加个统计功能:重命名成功率
验收标准:
- 能跑起来
- python rename.py --mode sequence photos/ 能把 photos 里的文件改成序号格式
- python rename.py --mode sequence photos/ --dry-run 只预览不实际改名
📚 总结
今天我们学了 3 个核心点:
1. pathlib 是 Python 里操作文件路径的标准方式,比 os.path 更直观
2. CSV/JSON 配置文件 让重命名规则和代码分离,灵活又方便
3. 观察者模式(事件监听)让程序可以「播报」自己的状态
下一章预告:你可能注意到了,项目 3 的代码里用了一堆嵌套的回调函数——每处理一个文件就要调用一次 listener,代码越写越「歪」。下一章我们要聊聊这个「回调地狱」问题,看看怎么用 Promise 来解决这个问题。敬请期待!
推荐资源:
- Python pathlib 官方文档
- 《Python编程:从入门到实践》 第 9 章
- 视频:B站「Python 文件操作合集」
互动钩子:你在工作中有没有遇到过需要批量处理文件的场景?是用什么工具解决的?评论区聊聊,说不定你的方法比我的还巧妙!老粉优先回复~

评论(0)