本文目录导读:

我来为您提供一个Python文件批量重命名工具,支持多种命名规则:
基础版本 - 支持多种命名模式
import os
import re
from pathlib import Path
class FileRenamer:
def __init__(self, directory):
"""
初始化
:param directory: 目标文件夹路径
"""
self.directory = Path(directory)
self.files = []
self.changes = []
def scan_files(self, pattern=None, extension=None):
"""
扫描文件
:param pattern: 文件名匹配模式(支持通配符)
:param extension: 文件扩展名过滤
"""
if extension:
self.files = list(self.directory.glob(f'*.{extension}'))
elif pattern:
self.files = list(self.directory.glob(pattern))
else:
self.files = list(self.directory.glob('*'))
# 只保留文件,不包含目录
self.files = [f for f in self.files if f.is_file()]
self.files.sort()
return self.files
def preview(self):
"""预览所有更改"""
print("\n=== 预览修改 ===")
for old_path, new_path in self.changes:
print(f"{old_path.name} -> {new_path.name}")
print(f"总计: {len(self.changes)} 个文件\n")
def execute(self, confirm=True):
"""
执行重命名
:param confirm: 是否确认执行
"""
if confirm:
self.preview()
response = input("确认执行?(y/n): ").lower()
if response != 'y':
print("已取消")
return False
for old_path, new_path in self.changes:
try:
old_path.rename(new_path)
print(f"✓ {old_path.name} -> {new_path.name}")
except Exception as e:
print(f"✗ 重命名 {old_path.name} 失败: {e}")
print(f"\n完成!重命名了 {len(self.changes)} 个文件")
return True
# === 命名规则 ===
def add_prefix(self, prefix):
"""添加前缀"""
self.changes = []
for file in self.files:
new_name = f"{prefix}{file.name}"
self.changes.append((file, file.with_name(new_name)))
return self
def add_suffix(self, suffix):
"""添加后缀(在扩展名之前)"""
self.changes = []
for file in self.files:
stem = file.stem
new_name = f"{stem}{suffix}{file.suffix}"
self.changes.append((file, file.with_name(new_name)))
return self
def replace_text(self, old_text, new_text):
"""替换文本"""
self.changes = []
for file in self.files:
new_name = file.name.replace(old_text, new_text)
self.changes.append((file, file.with_name(new_name)))
return self
def remove_text(self, text):
"""删除文本"""
return self.replace_text(text, "")
def change_extension(self, new_extension):
"""更改扩展名"""
if not new_extension.startswith('.'):
new_extension = f'.{new_extension}'
self.changes = []
for file in self.files:
new_name = f"{file.stem}{new_extension}"
self.changes.append((file, file.with_name(new_name)))
return self
def number_files(self, start=1, padding=2, prefix="", position="prefix"):
"""
按序号重命名
:param start: 起始序号
:param padding: 序号位数(补零)
:param prefix: 文件名前缀
:param position: 序号位置(prefix-在文件名前,suffix-在文件名后)
"""
self.changes = []
for i, file in enumerate(self.files):
num = str(start + i).zfill(padding)
stem = file.stem
ext = file.suffix
if position == "prefix":
new_name = f"{prefix}{num}_{stem}{ext}"
else: # suffix
new_name = f"{stem}_{num}{ext}"
self.changes.append((file, file.with_name(new_name)))
return self
def format_name(self, format_string):
"""
格式化命名
支持的占位符:
{index} - 序号
{name} - 原文件名(不含扩展名)
{ext} - 扩展名
{date} - 当前日期
{time} - 当前时间
"""
from datetime import datetime
now = datetime.now()
self.changes = []
for i, file in enumerate(self.files):
stem = file.stem
ext = file.suffix
new_name = format_string.format(
index=str(i+1).zfill(3),
name=stem,
ext=ext,
date=now.strftime("%Y%m%d"),
time=now.strftime("%H%M%S")
)
self.changes.append((file, file.with_name(new_name)))
return self
def regex_replace(self, pattern, replacement):
"""正则表达式替换"""
self.changes = []
for file in self.files:
new_name = re.sub(pattern, replacement, file.name)
self.changes.append((file, file.with_name(new_name)))
return self
def to_lowercase(self):
"""转为小写"""
self.changes = []
for file in self.files:
new_name = file.name.lower()
self.changes.append((file, file.with_name(new_name)))
return self
def to_uppercase(self):
"""转为大写"""
self.changes = []
for file in self.files:
new_name = file.name.upper()
self.changes.append((file, file.with_name(new_name)))
return self
def remove_spaces(self, replacement="_"):
"""删除空格"""
return self.replace_text(" ", replacement)
def undo(self):
"""撤销上次操作"""
if not self.changes:
print("没有可撤销的操作")
return
changed_files = [(new, old) for old, new in self.changes]
print("\n=== 撤销修改 ===")
for old_path, new_path in changed_files:
try:
if new_path.exists(): # 只撤销存在的文件
new_path.rename(old_path)
print(f"✓ {new_path.name} -> {old_path.name}")
except Exception as e:
print(f"✗ 撤销 {new_path.name} 失败: {e}")
self.changes = []
print("撤销完成")
# === 使用示例 ===
if __name__ == "__main__":
# 创建重命名器实例
renamer = FileRenamer("./test_files") # 替换为你的目标文件夹
# 扫描所有txt文件
renamer.scan_files(extension="txt")
# 应用命名规则(可以链式调用)
renamer.add_prefix("2024_") \
.remove_spaces() \
.to_lowercase()
# 预览修改
renamer.preview()
# 执行重命名
renamer.execute()
图形界面版本 (Tkinter)
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import os
from pathlib import Path
class BatchRenamerGUI:
def __init__(self, root):
self.root = root
self.root.title("批量文件重命名工具")
self.root.geometry("700x500")
self.directory = ""
self.files = []
self.changes = []
self.setup_ui()
def setup_ui(self):
# 主框架
main_frame = ttk.Frame(self.root, padding="10")
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# 文件夹选择
ttk.Label(main_frame, text="选择文件夹:").grid(row=0, column=0, sticky=tk.W)
self.dir_var = tk.StringVar()
ttk.Entry(main_frame, textvariable=self.dir_var, width=50).grid(row=0, column=1, padx=5)
ttk.Button(main_frame, text="浏览", command=self.browse_folder).grid(row=0, column=2)
# 文件过滤
ttk.Label(main_frame, text="文件类型:").grid(row=1, column=0, sticky=tk.W, pady=5)
self.ext_var = tk.StringVar(value="所有文件")
ext_combo = ttk.Combobox(main_frame, textvariable=self.ext_var, width=20)
ext_combo['values'] = ('所有文件', '.txt', '.jpg', '.png', '.pdf', '.docx', '.xlsx', '.csv')
ext_combo.grid(row=1, column=1, sticky=tk.W, pady=5)
ttk.Button(main_frame, text="扫描文件", command=self.scan_files).grid(row=1, column=2, pady=5)
# 文件列表
ttk.Label(main_frame, text="文件列表:").grid(row=2, column=0, sticky=tk.W, pady=5)
self.file_listbox = tk.Listbox(main_frame, height=10, width=80)
self.file_listbox.grid(row=3, column=0, columnspan=3, pady=5)
# 滚动条
scrollbar = ttk.Scrollbar(main_frame, orient=tk.VERTICAL, command=self.file_listbox.yview)
scrollbar.grid(row=3, column=3, sticky=(tk.N, tk.S))
self.file_listbox.configure(yscrollcommand=scrollbar.set)
# 命名规则框架
rules_frame = ttk.LabelFrame(main_frame, text="命名规则", padding="5")
rules_frame.grid(row=4, column=0, columnspan=3, pady=10, sticky=(tk.W, tk.E))
# 添加前缀
ttk.Label(rules_frame, text="添加前缀:").grid(row=0, column=0, padx=5)
self.prefix_var = tk.StringVar()
ttk.Entry(rules_frame, textvariable=self.prefix_var, width=15).grid(row=0, column=1)
ttk.Button(rules_frame, text="应用", command=lambda: self.apply_rule("prefix")).grid(row=0, column=2, padx=5)
# 添加后缀
ttk.Label(rules_frame, text="添加后缀:").grid(row=0, column=3, padx=5)
self.suffix_var = tk.StringVar()
ttk.Entry(rules_frame, textvariable=self.suffix_var, width=15).grid(row=0, column=4)
ttk.Button(rules_frame, text="应用", command=lambda: self.apply_rule("suffix")).grid(row=0, column=5, padx=5)
# 替换文本
ttk.Label(rules_frame, text="替换:").grid(row=1, column=0, padx=5, pady=5)
self.replace_old_var = tk.StringVar()
ttk.Entry(rules_frame, textvariable=self.replace_old_var, width=10).grid(row=1, column=1, pady=5)
ttk.Label(rules_frame, text="→").grid(row=1, column=2)
self.replace_new_var = tk.StringVar()
ttk.Entry(rules_frame, textvariable=self.replace_new_var, width=10).grid(row=1, column=3, pady=5)
ttk.Button(rules_frame, text="替换", command=lambda: self.apply_rule("replace")).grid(row=1, column=4, pady=5)
# 序号重命名
ttk.Label(rules_frame, text="序号:").grid(row=2, column=0, padx=5)
self.num_start_var = tk.StringVar(value="1")
ttk.Spinbox(rules_frame, from_=0, to=999, textvariable=self.num_start_var, width=5).grid(row=2, column=1)
ttk.Label(rules_frame, text="位数:").grid(row=2, column=2)
self.num_padding_var = tk.StringVar(value="2")
ttk.Spinbox(rules_frame, from_=1, to=5, textvariable=self.num_padding_var, width=5).grid(row=2, column=3)
ttk.Button(rules_frame, text="编号", command=lambda: self.apply_rule("number")).grid(row=2, column=4)
# 操作按钮
button_frame = ttk.Frame(main_frame)
button_frame.grid(row=5, column=0, columnspan=3, pady=10)
ttk.Button(button_frame, text="预览修改", command=self.preview_changes).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="执行重命名", command=self.execute_rename).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="重置", command=self.reset).pack(side=tk.LEFT, padx=5)
# 状态栏
self.status_var = tk.StringVar(value="就绪")
status_bar = ttk.Label(main_frame, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
status_bar.grid(row=6, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=5)
def browse_folder(self):
directory = filedialog.askdirectory()
if directory:
self.dir_var.set(directory)
self.scan_files()
def scan_files(self):
directory = self.dir_var.get()
if not directory:
messagebox.showerror("错误", "请选择文件夹")
return
self.files = []
self.file_listbox.delete(0, tk.END)
ext = self.ext_var.get()
if ext == "所有文件":
pattern = "*"
else:
pattern = f"*{ext}"
for file in Path(directory).glob(pattern):
if file.is_file():
self.files.append(file)
self.file_listbox.insert(tk.END, file.name)
self.status_var.set(f"找到 {len(self.files)} 个文件")
def apply_rule(self, rule_type):
if not self.files:
messagebox.showerror("错误", "请先扫描文件")
return
from datetime import datetime
self.changes = []
for i, file in enumerate(self.files):
stem = file.stem
ext = file.suffix
if rule_type == "prefix":
new_name = f"{self.prefix_var.get()}{file.name}"
elif rule_type == "suffix":
new_name = f"{stem}{self.suffix_var.get()}{ext}"
elif rule_type == "replace":
new_name = file.name.replace(self.replace_old_var.get(), self.replace_new_var.get())
elif rule_type == "number":
start = int(self.num_start_var.get())
padding = int(self.num_padding_var.get())
num = str(start + i).zfill(padding)
new_name = f"{num}_{file.name}"
else:
continue
self.changes.append((file, file.with_name(new_name)))
self.status_var.set(f"已应用规则,待处理 {len(self.changes)} 个文件")
self.preview_changes()
def preview_changes(self):
if not self.changes:
messagebox.showinfo("提示", "没有待处理的更改")
return
preview_text = "=== 预览修改 ===\n\n"
for old, new in self.changes:
preview_text += f" {old.name}\n → {new.name}\n\n"
preview_text += f"\n总计: {len(self.changes)} 个文件"
messagebox.showinfo("预览修改", preview_text)
def execute_rename(self):
if not self.changes:
messagebox.showerror("错误", "没有待执行的更改")
return
if not messagebox.askyesno("确认", f"确定要重命名 {len(self.changes)} 个文件吗?"):
return
success = 0
failed = 0
for old, new in self.changes:
try:
if not new.exists(): # 避免覆盖已有文件
old.rename(new)
success += 1
else:
failed += 1
messagebox.showwarning("警告", f"目标文件已存在: {new.name}")
except Exception as e:
failed += 1
print(f"重命名失败: {e}")
self.status_var.set(f"完成!成功: {success}, 失败: {failed}")
self.scan_files() # 刷新文件列表
self.changes = []
def reset(self):
self.changes = []
self.status_var.set("已重置")
if __name__ == "__main__":
root = tk.Tk()
app = BatchRenamerGUI(root)
root.mainloop()
命令行版本
#!/bin/bash
# file_renamer.sh - 命令行批量重命名工具
# 使用方法
usage() {
echo "批量文件重命名工具"
echo "用法: $0 [选项]"
echo ""
echo "选项:"
echo " -d DIR 目标文件夹 (默认: 当前目录)"
echo " -e EXT 文件扩展名过滤"
echo " -p PREFIX 添加前缀"
echo " -s SUFFIX 添加后缀"
echo " -r OLD NEW 替换文本"
echo " -n 序号重命名"
echo " -l 转为小写"
echo " -u 转为大写"
echo " --dry-run 预览模式"
echo ""
echo "示例:"
echo " $0 -d ./images -e jpg -p photo_"
echo " $0 -e txt -r 'old' 'new'"
echo " $0 -e pdf -n --dry-run"
}
# 解析参数
DIR="."
EXT=""
PREFIX=""
SUFFIX=""
REPLACE_OLD=""
REPLACE_NEW=""
NUMBER=false
LOWERCASE=false
UPPERCASE=false
DRY_RUN=false
while [[ $# -gt 0 ]]; do
case $1 in
-d) DIR="$2"; shift 2 ;;
-e) EXT="$2"; shift 2 ;;
-p) PREFIX="$2"; shift 2 ;;
-s) SUFFIX="$2"; shift 2 ;;
-r) REPLACE_OLD="$2"; REPLACE_NEW="$3"; shift 3 ;;
-n) NUMBER=true; shift ;;
-l) LOWERCASE=true; shift ;;
-u) UPPERCASE=true; shift ;;
--dry-run) DRY_RUN=true; shift ;;
-h|--help) usage; exit 0 ;;
*) echo "未知选项: $1"; usage; exit 1 ;;
esac
done
# 扫描文件
if [[ -n "$EXT" ]]; then
files=("$DIR"/*."$EXT")
else
files=("$DIR"/*)
fi
count=0
for file in "${files[@]}"; do
if [[ -f "$file" ]]; then
dir=$(dirname "$file")
basename=$(basename "$file")
filename="${basename%.*}"
extension="${basename##*.}"
newname="$basename"
# 应用规则
[[ -n "$PREFIX" ]] && newname="$PREFIX$newname"
[[ -n "$SUFFIX" ]] && newname="${filename}${SUFFIX}.${extension}"
[[ -n "$REPLACE_OLD" ]] && newname="${newname//$REPLACE_OLD/$REPLACE_NEW}"
if [[ "$LOWERCASE" == true ]]; then
newname=$(echo "$newname" | tr '[:upper:]' '[:lower:]')
fi
if [[ "$UPPERCASE" == true ]]; then
newname=$(echo "$newname" | tr '[:lower:]' '[:upper:]')
fi
if [[ "$NUMBER" == true ]]; then
count=$((count + 1))
num=$(printf "%02d" $count)
newname="${num}_${filename}.${extension}"
fi
# 预览或执行
if [[ "$DRY_RUN" == true ]]; then
echo "[预览] $basename -> $newname"
else
if [[ "$basename" != "$newname" ]]; then
mv "$file" "$dir/$newname"
echo "[执行] $basename -> $newname"
fi
fi
fi
done
echo "处理完成!"
使用方法
Python版本:
- 创建Python文件并运行
- 修改
directory为你的目标文件夹 - 调用不同的重命名方法
图形界面版本:
- 运行程序
- 选择文件夹
- 选择文件类型
- 应用不同的命名规则
- 预览并执行
命令行版本:
# 示例 ./file_renamer.sh -d ./photos -e jpg -p "vacation_" ./file_renamer.sh -e txt -r "old" "new" ./file_renamer.sh -e pdf -n --dry-run
支持的功能
- ✅ 添加前缀/后缀
- ✅ 替换/删除文本
- ✅ 序号重命名
- ✅ 大小写转换
- ✅ 正则表达式替换
- ✅ 撤销操作
- ✅ 预览修改
- ✅ 文件类型过滤
- ✅ 图形界面(可选)
需要更多定制功能或改进,请告诉我!
标签: 文件批量重命名工具
版权声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。