如何用Python按时间顺序重命名文件且不改变原有排列顺序
问题说明
我用Python编写了一款目录文件重命名应用,可根据文件扩展名把文件归类为Image、Video、Text、GIF、Audio、Unknown Extension六类。
原有逻辑为遍历目录获取全部文件、排序后按文件在列表中的索引分配序号完成重命名,但运行时会出现序号重复、序号跳漏的问题。
需求是实现按时间顺序重命名文件,且不改变文件原有显示顺序:例如重命名前排序第3的文件,重命名后仍保持第3的位置,效果示例如下:
目前初步梳理的实现思路如下:
- 按时间顺序获取文件夹内所有文件。
- 若文件名包含Image、video等特定关键词,将其归入对应关键词命名的列表中。
- 获取各分类列表的文件总数量。
- 循环遍历文件,按空格
' '分割文件名,校验文件名右侧的序号是否匹配当前循环值,若不匹配则保留名称前缀、替换为正确序号完成重命名。
问题复现代码
以下是可复现该问题的最小示例代码:
import os choice = "Subdirectories included" values = dict ({ "unknownCount" : 0, "textCount" : 0, "imageCount" : 0, "gifCount" : 0, "audioCount" : 0, "videoCount" : 0, }) os.chdir(location) if choice == "Subdirectories included": for root, dirs, files in os.walk(location): for i in sorted(files): fileName = os.path.join(root,i) if fileName.__contains__('.'): ext = fileName.split('.')[1] toR = Give(ext) + "." + ext toRename = root + toR try: os.rename(fileName, toRename) except: error = OSError print(error) def Give(ext): addUnknown = True if ext == "txt": toname = "\Text " + str(values["textCount"]) values["textCount"] += 1 addUnknown = False elif ext == "jpg": toname = "\Image " + str(values["imageCount"]) values["imageCount"] += 1 addUnknown = False elif ext == "jpeg": toname = "\Image " + str(values["imageCount"]) values["imageCount"] += 1 addUnknown = False elif ext == "png": toname = "\Image " + str(values["imageCount"]) values["imageCount"] += 1 addUnknown = False elif ext == "gif": toname = "\GIF " + str( values["gifCount"]) values["gifCount"] += 1 addUnknown = False elif ext == "mp3": toname = "\Audio " + str(values["audioCount"]) values["audioCount"] += 1 addUnknown = False elif ext == "ogg": toname = "\Audio " + str(values["audioCount"]) values["audioCount"] += 1 addUnknown = False elif ext == "wav": toname = "\Audio " + str(values["audioCount"]) values["audioCount"] += 1 addUnknown = False elif ext == "mkv": toname = "\Video " + str(values["videoCount"]) values["videoCount"] += 1 addUnknown = False elif ext == "avi": toname = "\Video " + str(values["videoCount"]) values["videoCount"] += 1 addUnknown = False elif ext == "mp4": toname = "\Video " + str(values["videoCount"]) values["videoCount"] += 1 addUnknown = False if addUnknown == True: toname = r"\Unknown Extension " + str(values["unknownCount"]) values["unknownCount"] += 1 return toname
问题原因与修复方案
原代码存在几个核心bug直接导致序号错乱:
- 遍历过程中边扫描边重命名,
os.walk读取的文件列表和磁盘实际文件不一致,直接引发重号、跳号 - 没有先完成全量文件的分类、排序再统一重命名,计数逻辑和遍历顺序不匹配
- 扩展名提取逻辑错误,遇到文件名本身带
.的文件(如report.v2.pdf)会取错扩展名 - 路径拼接用硬编码反斜杠,跨平台兼容性差,也容易出现路径拼接错误
- 没有处理重命名时的文件名冲突问题,直接重命名容易覆盖文件、抛错
修复后的可运行代码如下:
import os from typing import List, Dict # 扩展名与分类映射配置,新增格式直接在这里加即可 EXT_CATEGORY_MAP = { "txt": "Text", "jpg": "Image", "jpeg": "Image", "png": "Image", "gif": "GIF", "mp3": "Audio", "ogg": "Audio", "wav": "Audio", "mkv": "Video", "avi": "Video", "mp4": "Video" } def get_file_create_time(file_path: str) -> float: """获取文件创建时间,兼容Windows、macOS、Linux系统""" if os.name == 'nt': return os.path.getctime(file_path) else: stat = os.stat(file_path) try: return stat.st_birthtime except AttributeError: # 部分Linux系统无创建时间字段,用修改时间兜底 return stat.st_mtime def batch_rename(target_dir: str, include_subdir: bool = True): # 第一步:全量扫描文件,收集元信息,避免边扫边改导致的列表错乱 all_files: List[Dict] = [] if include_subdir: for root, _, files in os.walk(target_dir): for file in files: if file.startswith('.'): # 跳过隐藏文件 continue file_path = os.path.join(root, file) ext = os.path.splitext(file)[1].lower().lstrip('.') category = EXT_CATEGORY_MAP.get(ext, "Unknown Extension") all_files.append({ "path": file_path, "root": root, "ext": ext, "category": category, "create_time": get_file_create_time(file_path) }) else: for file in os.listdir(target_dir): file_path = os.path.join(target_dir, file) if not os.path.isfile(file_path) or file.startswith('.'): continue ext = os.path.splitext(file)[1].lower().lstrip('.') category = EXT_CATEGORY_MAP.get(ext, "Unknown Extension") all_files.append({ "path": file_path, "root": target_dir, "ext": ext, "category": category, "create_time": get_file_create_time(file_path) }) # 按创建时间升序排序,保证重命名后顺序和系统时间排序的显示顺序完全一致 all_files.sort(key=lambda x: x["create_time"]) # 第二步:初始化分类计数器 category_counter: Dict[str, int] = { "Text": 0, "Image": 0, "GIF": 0, "Audio": 0, "Video": 0, "Unknown Extension": 0 } # 第三步:先把所有文件改为临时名称,彻底避免重命名过程中的文件名冲突 temp_name_map = [] for idx, file_info in enumerate(all_files): temp_path = os.path.join(file_info["root"], f"__temp_rename_{idx}.{file_info['ext']}") os.rename(file_info["path"], temp_path) temp_name_map.append((temp_path, file_info)) # 第四步:统一重命名为最终格式,序号连续无重复 for temp_path, file_info in temp_name_map: category = file_info["category"] current_num = category_counter[category] final_name = f"{category} {current_num}.{file_info['ext']}" final_path = os.path.join(file_info["root"], final_name) os.rename(temp_path, final_path) category_counter[category] += 1 print(f"处理完成: {final_path}") if __name__ == "__main__": # 替换为需要处理的目标文件夹路径 TARGET_PATH = r"替换为你的目标文件夹路径" batch_rename(TARGET_PATH, include_subdir=True)
内容的提问来源于stack exchange,提问作者Harsh
相关产品推荐
相关产品推荐

