学径XUEJING · 个人学习平台通知设置

课程库 AI 主线 文件与自动化脚本 › 第 5

项目课:文件批量整理器 —— 批量重命名 + 生成文件清单表

项目课90 分钟

本课导读

项目时间。本单元四课的功夫(文本文件、CSV、JSON、pathlib)全部上桌,目标是一个拿得出手的自动化工具:把一个 30 个文件的灾难现场文件夹,一键变成"按类型归档 + 统一命名 + 附清单报表"的整洁目录。做完它,你就有了第二件作品集材料——而且是那种一句话就能向非技术面试官说清价值的:"我写了个脚本,把手工两小时的整理活变成了一秒"。预计 90 分钟。

项目课的规矩(重温)

  • 分版本推进:V1 最小可跑 → V2 加功能 → V3 出报表——每个版本都能运行,卡住时退回上一版
  • 先自己写再看参考:每步先按提示动手,参考实现是对照物不是抄写本
  • 验收清单说了算:最后逐条勾验收,全过才算交付

需求分析:像工程师一样开工

客户(就是三个月后的你):下载文件夹积了 30 个文件,报销单、截图、合同、数据表混作一团,找东西全靠翻。

需求:写一个整理器,运行一次完成——

  1. 按文件类型归档到子文件夹(jpg、pdf、csv……各回各家)
  2. 归档时统一改名:类型-序号-原名(如 pdf-01-报销单三月.pdf),序号按名称排序
  3. 生成一份 清单.csv:记录每个文件的原名、新位置、字数
  4. 支持彩排模式(上节课的职业规矩,本项目硬性要求)

先造出灾难现场(每次运行练习前都跑一遍它):

Python

random.seed(42) 让"随机"变得可复现——同样的种子长出同样的随机数,测试和验收就有了固定靶子。)

V1:最小可跑版——只归档

万丈高楼第一版:只做"按后缀搬家",改名和报表都先不管。上节课的组合拳预演几乎原样可用:

Python
✅ V1 参考实现
from pathlib import Path

root = Path("chaos_v1")        # 与上方 Playground 同一现场(先跑过 make_mess)
root.mkdir(exist_ok=True)

for p in sorted(root.iterdir()):
    if p.is_dir():
        continue
    kind = p.suffix.lstrip(".")
    target_dir = root / kind
    target_dir.mkdir(exist_ok=True)
    p.rename(target_dir / p.name)

for sub in sorted(root.iterdir()):
    if sub.is_dir():
        print(f"{sub.name}/: {len(list(sub.iterdir()))} 个")

V2:函数化 + 统一改名 + 彩排开关

V1 是一次性的脚本,V2 升级成可复用的工具函数

  • 包成 organize(folder, dry_run=True)
  • 搬家时顺便改名:类型-序号-原名(序号在各类型内分别从 01 编起)
  • dry_run=True 只打印计划,不动文件
Python
💡 提示:分组思路

第一步"按后缀分组"就是老朋友"遍历 + 字典收集":groups.setdefault(kind, []).append(p) 或 if-in 老写法。第二步对每组 for i, p in enumerate(sorted(组), 1)f"{kind}-{i:02d}-{p.name}"。dry_run 的分叉参考上节课练习 3。

✅ V2 参考实现
from pathlib import Path

Path("chaos_v2").mkdir(exist_ok=True)   # 与上方 Playground 同一现场(先跑过 make_mess)

def organize(folder, dry_run=True):
    root = Path(folder)

    # 分组:后缀 → 文件列表
    groups = {}
    for p in sorted(root.iterdir()):
        if p.is_dir():
            continue
        kind = p.suffix.lstrip(".")
        groups.setdefault(kind, []).append(p)

    # 逐组编号搬家
    for kind, files in sorted(groups.items()):
        target_dir = root / kind
        for i, p in enumerate(files, 1):
            new_name = f"{kind}-{i:02d}-{p.name}"
            if dry_run:
                print(f"[彩排] {p.name} → {kind}/{new_name}")
            else:
                target_dir.mkdir(exist_ok=True)
                p.rename(target_dir / new_name)
                print(f"{p.name} → {kind}/{new_name}")

organize("chaos_v2")                 # 彩排
organize("chaos_v2", dry_run=False)  # 放行

新面孔 setdefault(k, []):等价于"键不存在就先放个空列表",是 groups.get(k, []) + 赋值的一步到位版。

V3:完整交付——加清单报表

最后一块:归档的同时收集档案,写出 清单.csv(列:原名、新位置、字数)。这是"读 → 加工 → 写"三段式的最终形态:

Python
✅ V3 参考实现(完整最终版)
from pathlib import Path
import csv

Path("chaos_v3").mkdir(exist_ok=True)   # 与上方 Playground 同一现场(先跑过 make_mess)

def organize(folder, dry_run=True):
    """按类型归档 + 统一改名 + 生成清单报表;dry_run=True 时只彩排"""
    root = Path(folder)

    groups = {}
    for p in sorted(root.iterdir()):
        if p.is_dir():
            continue
        kind = p.suffix.lstrip(".")
        groups.setdefault(kind, []).append(p)

    records = []
    for kind, files in sorted(groups.items()):
        target_dir = root / kind
        for i, p in enumerate(files, 1):
            new_name = f"{kind}-{i:02d}-{p.name}"
            if dry_run:
                print(f"[彩排] {p.name} → {kind}/{new_name}")
                continue
            target_dir.mkdir(exist_ok=True)
            size = len(p.read_text(encoding="utf-8"))   # 搬家前量字数
            p.rename(target_dir / new_name)
            records.append({"原名": p.name, "新位置": f"{kind}/{new_name}", "字数": size})

    if not dry_run:
        with open(root / "清单.csv", "w", encoding="utf-8", newline="") as f:
            writer = csv.writer(f)
            writer.writerow(["原名", "新位置", "字数"])
            for r in records:
                writer.writerow([r["原名"], r["新位置"], r["字数"]])
        print(f"完成:归档 {len(records)} 个文件,清单已生成")

organize("chaos_v3", dry_run=False)
print((Path("chaos_v3") / "清单.csv").read_text(encoding="utf-8")[:200])

验收与魔改(作品集第二件)

分步操作

魔改方向(选做,做了就写进作品集说明):

  • 清单顺便写一份 清单.json(json.dump 一行的事,两种格式各有客户)
  • 给不认识的后缀兜底:归入 其他/ 文件夹
  • 用 U1.2 的功夫把整理器包成 Organizer 类:Organizer("chaos").preview() / .run()

提示

把这个项目讲给非技术朋友听,练一句话版本:"公司同事的下载文件夹平均积压几百个文件,我这个脚本一秒完成归类、改名、出清单。"——转行面试里,能说清价值的小工具,胜过说不清价值的大项目

📝 随堂测验

随堂测验0 / 5 题正确

1. make_mess 里 random.seed(42) 的作用是?

2. V2 里“按后缀分组”用的 groups.setdefault(kind, []).append(p) 等价于?

3. 为什么清单要在 rename 之前读字数(size = len(p.read_text(...)))?

4. “重复运行整理器不报错”这条验收,主要靠什么保证?

5. 本项目的彩排模式(dry_run)体现的工程思想是?

本课小结

  • 完整走通"需求 → V1 最小可跑 → V2 函数化+彩排 → V3 报表交付"的项目节奏
  • 四课功夫合流:pathlib 扫描搬家 + 字典分组 + enumerate 编号 + csv 出清单
  • 三条职业规矩落地:彩排再动手、先取信息再动文件、重复运行也安全
  • 作品集第二件到手,配一句"说得清价值"的介绍词

下一课单元关卡:综合测验收网整单元,外加一份帮你把项目"讲出去"的验收自查。

划选正文任意文字可高亮、批注或加入复习卡

讨论

载入中…