You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python按时间顺序重命名文件且不改变原有排列顺序

问题说明

我用Python编写了一款目录文件重命名应用,可根据文件扩展名把文件归类为Image、Video、Text、GIF、Audio、Unknown Extension六类。
原有逻辑为遍历目录获取全部文件、排序后按文件在列表中的索引分配序号完成重命名,但运行时会出现序号重复、序号跳漏的问题。
需求是实现按时间顺序重命名文件,且不改变文件原有显示顺序:例如重命名前排序第3的文件,重命名后仍保持第3的位置,效果示例如下:
目标效果

目前初步梳理的实现思路如下:

  1. 按时间顺序获取文件夹内所有文件。
  2. 若文件名包含Image、video等特定关键词,将其归入对应关键词命名的列表中。
  3. 获取各分类列表的文件总数量。
  4. 循环遍历文件,按空格' '分割文件名,校验文件名右侧的序号是否匹配当前循环值,若不匹配则保留名称前缀、替换为正确序号完成重命名。
问题复现代码

以下是可复现该问题的最小示例代码:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 20:19:01