当前位置: 首页 > ai >正文

批量修改文件名前后缀

前言

就是心血来潮想实现一个批量修改文件名的功能,因为在备考期间因为需要下载一些电子档的资料,然后下载的部分资料会有自己的前缀,但是自己看着有点不舒服,因为文件也比较多,所以想能不能通过代码的形式对于文件名进行批量的修改。

代码划分

功能实现

要实现的功能其实很简单,通过Python代码获取文件夹的路径,通过循环处理该文件夹中所有文件的名称,之后对文件名进行更新即可,整个撰写代码是通过人工加上ai的方式进行的。

添加

 添加前后缀,直接对原始的字符串进行拼接即可,中间通过分割符来分割,对于添加的字符串不论中文还是英文都需要满足windows系统文件命名的规范,项目中通过ai实现了验证过程,但是可能还是不完善。

# 添加前缀 
new_base = f"{content}{selected_separator}{base_name}"
# 添加后缀
new_base = f"{base_name}{selected_separator}{content}"

 重命名操作

# 执行重命名操作
try:os.replace(file_path, new_path)  # 自动覆盖已存在的文件print(f"成功重命名:{filename} -> {new_name}")
except Exception as e:messagebox.showerror("重命名错误",f"无法重命名 {filename}:\n{str(e)}\n""可能原因:\n""1. 文件正在被其他程序使用\n""2. 没有写入权限\n""3. 文件名包含系统保留字符")break  # 遇到错误中止处理

删除

删除前后缀即对字符串进行分割操作。 

Python中的split()方法使用大全_split在python如何运用-CSDN博客https://blog.csdn.net/2402_86735906/article/details/147359776

# 删除前缀使用split(从左分割)
parts = base_name.split(f"{content}{selected_separator}", 1)
# 删除后缀使用rsplit(从右分割)
parts = base_name.rsplit(f"{selected_separator}{content}", 1)

没想到这么快在使用过程中出现问题 ,这个直接分割可能出现中间截取的情况,比如“专题01 物质的组成、性质、分类与化学用语(讲)(原卷+解析版)”这个文件名,如果我是要删除后缀“ 物质”,这个肯定是无法找到的,但是如果是截取,他则会只保留“专题01”。

修改代码如下:

            elif selected_mode == "删除前缀":# 格式验证:必须包含分割符且分割符在content之后if base_name.startswith(f"{content}{selected_separator}"):# 计算前缀长度时考虑中文等宽字符问题prefix_length = len(content) + len(selected_separator)new_base = base_name[prefix_length:]# 增加空文件名校验if not new_base:print(f"警告:删除前缀后文件名为空,跳过 {filename}")continueelse:print(f"未找到匹配前缀:{content}{selected_separator}")continueelif selected_mode == "删除后缀":# 格式验证:必须严格以【分隔符+内容】结尾suffix_pattern = f"{selected_separator}{content}"if base_name.endswith(suffix_pattern):# 计算后缀长度(考虑多语言字符)suffix_length = len(suffix_pattern)new_base = base_name[:-suffix_length]# 空文件名防御机制if not new_base.strip():  # 处理纯空白字符情况print(f"危险操作:删除后缀后文件名为空,跳过 {filename}")continueelse:print(f"未找到匹配后缀:{suffix_pattern}")continue

当然在删除前还需要对文件名进行验证,看是否有符合的前后缀以及是否和其他文件名冲突。

# 前缀验证
if f"{content}{selected_separator}" in base_name:

 不论是添加还是删除操作都需要检测文件名是否冲突。

# 检测文件名冲突
if os.path.exists(new_path):# 弹出二次确认对话框confirm = messagebox.askyesno("确认覆盖",f"文件 '{new_name}' 已存在!\n"f"原文件:{filename}\n"f"新文件:{new_name}\n\n""是否覆盖已有文件?",icon='warning')if not confirm:print(f"跳过已存在文件:{new_name}")continue

图形界面

定义界面

对于这些框架的代码,使用ai还是比较容易实现,就是调试起来有点麻烦,不能无脑丢给ai,还需要自己进行分析,通过电脑界面来对窗口的界面和位置进行设置。

# 定义界面框架
class Frame:# 创建窗口window = tk.Tk()# 在Frame类初始化前添加样式配置代码style = ttk.Style()# 设置主题为clamstyle.theme_use('clam')# 配置下拉框主体样式style.configure('Centered.TCombobox', justify="center", foreground="#2c3e50",fieldbackground="white", padding=(0, 15), anchor="center", state="readonly")# 配置下拉列表的样式style.configure('Centered.TCombobox.Listbox', foreground="#2c3e50",rowheight=30, anchor="center")# 设置标题window.title("文件批量重命名工具")# 获取屏幕大小screen_width = window.winfo_screenwidth()screen_height = window.winfo_screenheight()# 计算所需窗口的相对大小relative_width = int(screen_width * 0.5)relative_height = int(screen_height * 0.5)# 计算窗口位于屏幕中央的坐标relative_x = (screen_width - relative_width) // 2relative_y = (screen_height - relative_height) // 2# 应用窗口尺寸和位置(格式:宽度x高度+X坐标+Y坐标)window.geometry(f"{relative_width}x{relative_height}+{relative_x}+{relative_y}")# 应用组件module(window)# 运行窗口window.mainloop()

定义组件

主要就是涉及到了文本框、按钮和下拉框的布局,需要对文本框修改成只读的属性,文件夹路径

操作模式和分割符就没有进行是否规范的判断。

【python】tkinter简要教程-CSDN博客https://blog.csdn.net/weixin_43764974/article/details/145738021

def module(window):global path_entry, mode_var, separator_var  # 声明全局变量(因为后续在其他函数中需要使用到这些参数)# 初始化默认值mode_var = tk.StringVar(value="添加前缀")separator_var = tk.StringVar(value=" _下划线")########################################################################################################## 新增创建路径选择容器(顶部区域)path_frame = tk.Frame(window)path_frame.pack(side=tk.TOP, pady=20)# 创建只读文本框path_entry = tk.Entry(path_frame, width=65, font=("仿宋", 12, "bold"), state="readonly",readonlybackground="white")path_entry.pack(side=tk.LEFT, padx=5, ipady=25)# 创建浏览按钮browse_button = tk.Button(path_frame, text="获取文件夹名", width=100, height=2, bg="#2196F3", fg="white",font=("仿宋", 18, "bold"), command=lambda: browse_folder(path_entry))browse_button.pack(side=tk.RIGHT, padx=5)########################################################################################################## 增加操作模式选择区域mode_frame = tk.Frame(window)mode_frame.pack(fill=tk.X, pady=20)# 模式选择标签mode_label = tk.Label(mode_frame, text="操作模式:", font=("仿宋", 28, "bold"), width=15)mode_label.pack(side=tk.LEFT, padx=5)# 下拉框选项数据mode_options = ["添加前缀", "添加后缀", "删除前缀", "删除后缀"]# 创建下拉框(要用textvariable=mode_var绑定数据,不然不会更新)mode_combobox = ttk.Combobox(mode_frame, values=mode_options, width=30, style='Centered.TCombobox',justify="center", state="readonly", font=("仿宋", 28, "bold"), textvariable=mode_var)mode_combobox.pack(side=tk.LEFT, padx=15)mode_combobox.current(0)  # 设置默认选中########################################################################################################## 增加常见分割符区域mode_frame = tk.Frame(window)mode_frame.pack(fill=tk.X, pady=20)# 模式选择标签mode_label = tk.Label(mode_frame, text="常见分割符", font=("仿宋", 28, "bold"), width=15, anchor="center")mode_label.pack(side=tk.LEFT, padx=5)# 下拉框选项数据mode_options = ["_下划线", "-连字符", ".点号", " 空格"]# 创建下拉框(新增绑定textvariable=separator_var)mode_combobox = ttk.Combobox(mode_frame, values=mode_options, width=30, style='Centered.TCombobox',font=("仿宋", 28, "bold"), state="readonly", justify="center",textvariable=separator_var)mode_combobox.pack(side=tk.LEFT, padx=15)mode_combobox.current(0)  # 设置默认选中########################################################################################################## 给界面添加确认和取消的按钮# 创建按钮容器框架(实现更灵活的布局控制)button_frame = tk.Frame(window)# 固定在窗口底部并设置纵向间距button_frame.pack(side=tk.BOTTOM, pady=20)# 创建确认和取消按钮confirm_button = tk.Button(button_frame, text="确认", width=12, height=2, bg="#4CAF50", fg="white",font=("仿宋", 18, "bold"), command=lambda: on_confirm())  # 绑定确认事件处理cancel_button = tk.Button(button_frame, text="取消", width=12, height=2, bg="#F44336", fg="white",font=("仿宋", 18, "bold"),command=lambda: window.destroy())  # 直接绑定关闭窗口的事件# 确认按钮(添加pack布局)confirm_button.pack(side=tk.LEFT, padx=10, ipady=5)# 取消按钮(添加pack布局)cancel_button.pack(side=tk.RIGHT, padx=10, ipady=5)

系统界面如下图所示 

 完整代码

完整的代码如下,若有什么错误可以在评论区留言,后续会进行更新

import os
import tkinter as tk
from tkinter import filedialog
from tkinter import ttk
from tkinter import messagebox
from tkinter import simpledialog# 验证文件名是否符合Windows规范(中文增强版)
# 修改现有is_valid_filename函数
def is_valid_filename(text: str) -> bool:# 非法字符检测(保持原逻辑)illegal_chars = set('\\/:*?"<>|')if any(char in illegal_chars for char in text):return False# 保留名称检测(保持原逻辑)reserved_names = {'CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3','COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9','LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6','LPT7', 'LPT8', 'LPT9'}if text.upper() in reserved_names:return False# 增强中文处理# 1. 检查全角字符是否包含非法字符(中文输入法可能输入全角字符)fullwidth_illegal = set('\/:*?"<>|')  # 全角非法字符if any(char in fullwidth_illegal for char in text):return False# 2. 增强长度检测(基于字符数而非字节数)# Windows允许最多255个字符(包括中文)if len(text) > 255:return False# 3. 检查首尾空格(中文文件名常见问题)if text.strip() != text:return False# 4. 检查末尾点号(.test.txt. 这种形式)if text.endswith('.') or text.startswith('.'):return Falsereturn True# 用文本框获取添加或者删除的字符串
def getTextbox(parent_window=None):# 通过全局控件获取父窗口global path_entry  # 声明使用全局路径输入框parent_window = path_entry.winfo_toplevel()  # 获取输入框所在的顶级窗口# 添加循环输入机制while True:content = simpledialog.askstring("输入内容","请输入要添加的字符串(不能包含 \\/:*?\"<>| 等非法字符):",  # 添加提示parent=parent_window)print("输入:", content)# 用户取消输入if content is None:return None# 验证逻辑if not content:messagebox.showerror("错误", "输入不能为空!")elif not is_valid_filename(content):messagebox.showerror("错误",f"文件名不合法!\n"f"1. 请勿使用:\\ / : * ? \" < > | 及其全角形式\n"f"2. 不要使用CON、PRN等保留名称\n"f"3. 长度不超过255字符(当前:{len(content)})\n"f"4. 首尾不能有空格\n"f"5. 不能以点号开头或结尾")else:return content  # 合法输入退出循环# 新增文件夹浏览函数
def browse_folder(entry_widget):"""打开文件夹选择对话框"""folder_path = filedialog.askdirectory(title='请选择要处理的文件夹')if folder_path:# 清空并更新文本框内容entry_widget.config(state='normal')entry_widget.delete(0, tk.END)entry_widget.insert(0, folder_path)entry_widget.config(state='readonly')# 可选:自动滚动到末尾entry_widget.xview_moveto(1)# 定义确认事件处理
def on_confirm():# print("确认")# 获取所有输入数据folder_path = path_entry.get()selected_mode = mode_var.get()selected_separator = separator_var.get()[0]  # 只要前一个英文字符即可# 验证数据完整性if not folder_path:messagebox.showerror("警告", "请先选择文件夹!")# 选择文件夹名browse_folder(path_entry)return# 打印结果print(f"文件夹路径:{folder_path}")print(f"操作模式:{selected_mode}")print(f"分隔符:{selected_separator}")# 获取要添加或者删除的文字,需要在循环外就确认了,因为只需要确认一次即可content = getTextbox()# 输入为None不能继续运行了,否则会把None当做字符串进行拼接if content is None:return# 新增文件处理逻辑(Listdir获取文件夹中所有文件和文件夹名称组成的列表)for filename in os.listdir(folder_path):# 获取文件路径(join拼接路径)file_path = os.path.join(folder_path, filename)if os.path.isfile(file_path):# 打印文件路径# print(f"正在处理文件:{file_path}")# 分割文件名和拓展名base_name, ext = os.path.splitext(filename)# 根据模式处理文件名if selected_mode == "添加前缀":# 直接进行拼接操作new_base = f"{content}{selected_separator}{base_name}"# print(new_base)elif selected_mode == "添加后缀":new_base = f"{base_name}{selected_separator}{content}"elif selected_mode == "删除前缀":# 格式验证:必须包含分割符且分割符在content之后if base_name.startswith(f"{content}{selected_separator}"):# 计算前缀长度时考虑中文等宽字符问题prefix_length = len(content) + len(selected_separator)new_base = base_name[prefix_length:]# 增加空文件名校验if not new_base:print(f"警告:删除前缀后文件名为空,跳过 {filename}")continueelse:print(f"未找到匹配前缀:{content}{selected_separator}")continueelif selected_mode == "删除后缀":# 格式验证:必须严格以【分隔符+内容】结尾suffix_pattern = f"{selected_separator}{content}"if base_name.endswith(suffix_pattern):# 计算后缀长度(考虑多语言字符)suffix_length = len(suffix_pattern)new_base = base_name[:-suffix_length]# 空文件名防御机制if not new_base.strip():  # 处理纯空白字符情况print(f"危险操作:删除后缀后文件名为空,跳过 {filename}")continueelse:print(f"未找到匹配后缀:{suffix_pattern}")continue# 拼接新的文件名new_name = f"{new_base}{ext}"# 拼接新的文件路径new_path = os.path.join(folder_path, new_name)# 检测文件名冲突if os.path.exists(new_path):# 弹出二次确认对话框confirm = messagebox.askyesno("确认覆盖",f"文件 '{new_name}' 已存在!\n"f"原文件:{filename}\n"f"新文件:{new_name}\n\n""是否覆盖已有文件?",icon='warning')if not confirm:print(f"跳过已存在文件:{new_name}")continue# 执行重命名操作try:os.replace(file_path, new_path)  # 自动覆盖已存在的文件print(f"成功重命名:{filename} -> {new_name}")except Exception as e:messagebox.showerror("重命名错误",f"无法重命名 {filename}:\n{str(e)}\n""可能原因:\n""1. 文件正在被其他程序使用\n""2. 没有写入权限\n""3. 文件名包含系统保留字符")break  # 遇到错误中止处理# 定义界面展示的组件
def module(window):global path_entry, mode_var, separator_var  # 声明全局变量(因为后续在其他函数中需要使用到这些参数)# 初始化默认值mode_var = tk.StringVar(value="添加前缀")separator_var = tk.StringVar(value=" _下划线")########################################################################################################## 新增创建路径选择容器(顶部区域)path_frame = tk.Frame(window)path_frame.pack(side=tk.TOP, pady=20)# 创建只读文本框path_entry = tk.Entry(path_frame, width=65, font=("仿宋", 12, "bold"), state="readonly",readonlybackground="white")path_entry.pack(side=tk.LEFT, padx=5, ipady=25)# 创建浏览按钮browse_button = tk.Button(path_frame, text="获取文件夹名", width=100, height=2, bg="#2196F3", fg="white",font=("仿宋", 18, "bold"), command=lambda: browse_folder(path_entry))browse_button.pack(side=tk.RIGHT, padx=5)########################################################################################################## 增加操作模式选择区域mode_frame = tk.Frame(window)mode_frame.pack(fill=tk.X, pady=20)# 模式选择标签mode_label = tk.Label(mode_frame, text="操作模式:", font=("仿宋", 28, "bold"), width=15)mode_label.pack(side=tk.LEFT, padx=5)# 下拉框选项数据mode_options = ["添加前缀", "添加后缀", "删除前缀", "删除后缀"]# 创建下拉框(要用textvariable=mode_var绑定数据,不然不会更新)mode_combobox = ttk.Combobox(mode_frame, values=mode_options, width=30, style='Centered.TCombobox',justify="center", state="readonly", font=("仿宋", 28, "bold"), textvariable=mode_var)mode_combobox.pack(side=tk.LEFT, padx=15)mode_combobox.current(0)  # 设置默认选中########################################################################################################## 增加常见分割符区域mode_frame = tk.Frame(window)mode_frame.pack(fill=tk.X, pady=20)# 模式选择标签mode_label = tk.Label(mode_frame, text="常见分割符", font=("仿宋", 28, "bold"), width=15, anchor="center")mode_label.pack(side=tk.LEFT, padx=5)# 下拉框选项数据mode_options = ["_下划线", "-连字符", ".点号", " 空格"]# 创建下拉框(新增绑定textvariable=separator_var)mode_combobox = ttk.Combobox(mode_frame, values=mode_options, width=30, style='Centered.TCombobox',font=("仿宋", 28, "bold"), state="readonly", justify="center",textvariable=separator_var)mode_combobox.pack(side=tk.LEFT, padx=15)mode_combobox.current(0)  # 设置默认选中########################################################################################################## 给界面添加确认和取消的按钮# 创建按钮容器框架(实现更灵活的布局控制)button_frame = tk.Frame(window)# 固定在窗口底部并设置纵向间距button_frame.pack(side=tk.BOTTOM, pady=20)# 创建确认和取消按钮confirm_button = tk.Button(button_frame, text="确认", width=12, height=2, bg="#4CAF50", fg="white",font=("仿宋", 18, "bold"), command=lambda: on_confirm())  # 绑定确认事件处理cancel_button = tk.Button(button_frame, text="取消", width=12, height=2, bg="#F44336", fg="white",font=("仿宋", 18, "bold"),command=lambda: window.destroy())  # 直接绑定关闭窗口的事件# 确认按钮(添加pack布局)confirm_button.pack(side=tk.LEFT, padx=10, ipady=5)# 取消按钮(添加pack布局)cancel_button.pack(side=tk.RIGHT, padx=10, ipady=5)# 定义界面框架
class Frame:# 创建窗口window = tk.Tk()# 在Frame类初始化前添加样式配置代码style = ttk.Style()# 设置主题为clamstyle.theme_use('clam')# 配置下拉框主体样式style.configure('Centered.TCombobox', justify="center", foreground="#2c3e50",fieldbackground="white", padding=(0, 15), anchor="center", state="readonly")# 配置下拉列表的样式style.configure('Centered.TCombobox.Listbox', foreground="#2c3e50",rowheight=30, anchor="center")# 设置标题window.title("文件批量重命名工具")# 获取屏幕大小screen_width = window.winfo_screenwidth()screen_height = window.winfo_screenheight()# 计算所需窗口的相对大小relative_width = int(screen_width * 0.5)relative_height = int(screen_height * 0.5)# 计算窗口位于屏幕中央的坐标relative_x = (screen_width - relative_width) // 2relative_y = (screen_height - relative_height) // 2# 应用窗口尺寸和位置(格式:宽度x高度+X坐标+Y坐标)window.geometry(f"{relative_width}x{relative_height}+{relative_x}+{relative_y}")# 应用组件module(window)# 运行窗口window.mainloop()def main():# 创建窗口Frame()if __name__ == "__main__":main()

测试

原始文件名

选择“操作模式”以及“常见分割符”,后点击“确认”按钮,在输入框中输入需要添加或者删除的字符串。

pycharm中的输出:

结果如下图所示: 

添加后缀操作:

这里之前出现了一个问题,如果文本框没有输入的话,他会返回None,然后把None当做字符串进行拼接,这里需要避免这种情况。

# 输入为None不能继续运行了,否则会把None当做字符串进行拼接
if content is None:return

对于后续还可以通过修改代码批量修改文件的扩展名。

http://www.xdnf.cn/news/2979.html

相关文章:

  • 国内无法访问GitHub官网的问题解决
  • Cell Res | Stereo-seq揭示人类肝癌浸润区促进肝细胞-肿瘤细胞串扰、局部免疫抑制和肿瘤进展
  • 探索数学之美:分形几何之在线交互式曼德博集合动画演示工具
  • C++类与对象基础
  • 破局传统采购,连锁大药房打造一体化招采平台
  • 川土微电子全国产供应链且全面通过IBEE EMC认证的车规CAN收发器CA-IF1044AX-Q1
  • Mysql数据类型
  • 0.5 像素边框实现
  • Javscript 数组的常用方法有哪些?
  • 软实时如Windows,在工业领域的弊端
  • Game Booster汉化版:一键优化,畅享游戏
  • std::functional 类是干什么用的?
  • 项目实战-飞机大战【补档】
  • 【AI面试准备】模型自动化评估
  • C++学习:六个月从基础到就业——异常处理:机制与最佳实践
  • Qt5与现代OpenGL学习(三)纹理
  • 极狐GitLab 如何使用文件导出迁移项目和群组?
  • 机器学习day4-Knn+交叉验证api练习(预测facebook签到位置)
  • QT6链接mysql数据库
  • SQL实战:04之SQL中的分组问题求解
  • 深度学习·经典模型·VisionTransformer
  • 串口通信协议
  • (004)Excel 监视窗口
  • 系统分析师-第十三、十四章
  • 算法设计:分支限界法的基础原理与应用
  • Element:Cheack多选勾选效果逻辑判断
  • 区块链最佳框架:Truffle vs Hardhat vs Brownie
  • partition_pdf 和chunk_by_title 的区别
  • package.json文件中的 ^ 和 ~
  • DOM 事件的处理通常分为三个阶段:捕获、目标、冒泡【前端示例】