30 分钟上手 AtomCode:用它写一个 Python 批量整理文件/改名/生成报告小工具(新手教程)

写在前面:这篇文章适合谁?

如果你是第一次接触 AtomCode,想在不折腾复杂工程的前提下,用 30 分钟做出一个"能跑、可复现、可扩展"的小工具,那么这篇教程适合你。

本文将带你用 AtomCode 完成一个常见需求:

  • 扫描指定目录下的文件(仅当前层,不递归)
  • 按规则批量重命名(安全、可预演)
  • 输出一份整理报告(Markdown)

你将得到什么(最终效果)

最终你会得到一个 Python 脚本 file_organizer.py

  • 支持 dry-run 预演:只生成计划与报告,不改文件

  • 支持 apply 执行:真正改名,并生成报告

  • 报告包含统计与明细表格:原名 → 新名、状态、原因


    0. 准备工作(3 分钟)

    必备条件

    • 已安装 Python 3.9+(建议 3.11)
    • 已安装并能打开 AtomCode(或 AtomCodeAir)

    新建练习目录(避免误操作)

    建议新建一个干净目录,确保不会误改重要文件:

atomcode-file-organizer/

samples/

raw/

(放一些你要整理的测试文件)

file_organizer.py

复制代码
在 `samples/raw` 里放 5~10 个测试文件(图片、压缩包、文档都行),用于验证脚本效果。

---

1. 在 AtomCode 里创建脚本文件(2 分钟)

  1. 用 AtomCode 打开 atomcode-file-organizer/
  2. 新建文件:file_organizer.py

接下来让 AtomCode 帮你"搭骨架 + 补细节",你只需要把规则讲清楚并做最终确认。


2. 给 AtomCode 一段"可执行、可测试"的需求描述(5 分钟)

在 AtomCode 对话框里输入(建议直接复制):

请为我生成一个 Python 脚本 file_organizer.py,要求:

  1. 使用 argparse 接收参数:--root(必填,待整理目录)、--mode(dry-run/apply)、--report(可选,输出报告路径,默认 report.md
  2. 扫描 root 目录(只处理当前层,不递归),对文件做"安全重命名":
    • 新文件名规则:YYYYMMDD_序号_原始文件名清理版.原扩展名(序号从 001 开始)
    • 清理规则:去掉多余空格;把非字母数字、非中文、非 ._- 的字符替换为 _;连续 _ 合并为一个;文件名最长 80
  3. 冲突处理:若目标文件名已存在,则在序号后追加 -1-2 直到不冲突
  4. dry-run 只打印与写报告,不改文件;apply 才执行重命名
  5. 报告输出 Markdown:包含统计信息(总数、成功、跳过、失败)和详细表格(原名、新名、状态、原因)
  6. 仅使用标准库,兼容 Windows 路径
  7. 提供最少但关键的异常处理与可读输出

请先给出完整代码,再给出 2 组运行命令示例。

这一步的核心是:把规则写死。新手最常踩的坑就是"需求太口语",导致脚本看似能用,实际会在边界条件翻车。


3. 把 AtomCode 生成的代码写入文件(1 分钟)

将 AtomCode 输出的完整代码粘贴到 file_organizer.py 并保存。

如果它漏了某个函数或输出不全,不要让它重写一遍整个文件,推荐补充式提问:

  • "请补全缺失的 main() 并保持已有结构不变"
  • "请补全报告 Markdown 表格输出部分,保持参数不变"

4. 一份可直接运行的参考实现(仅标准库)

下面是一份满足上述需求的实现,你可以直接使用;如果你已经让 AtomCode 生成了代码,也可以用它来对照检查关键点是否一致(参数、模式、冲突处理、报告输出等)。

安全提醒:首次运行务必用 --mode dry-run

python 复制代码
import argparse
import datetime as dt
import os
import re
from dataclasses import dataclass
from pathlib import Path
from typing import List, Tuple


@dataclass
class ItemResult:
    src_name: str
    dst_name: str
    status: str  # success / skipped / failed
    reason: str


def sanitize_stem(stem: str, max_len: int = 80) -> str:
    s = stem.strip()
    s = re.sub(r"\s+", " ", s)
    s = re.sub(r"[^0-9A-Za-z\u4e00-\u9fff\.\_\- ]+", "_", s)
    s = s.replace(" ", "_")
    s = re.sub(r"_+", "_", s)
    s = s.strip("._-")

    if not s:
        s = "unnamed"

    if len(s) > max_len:
        s = s[:max_len].rstrip("._-")

    return s if s else "unnamed"


def resolve_conflict(dst: Path) -> Path:
    if not dst.exists():
        return dst

    parent = dst.parent
    stem = dst.stem
    suffix = dst.suffix

    i = 1
    while True:
        candidate = parent / f"{stem}-{i}{suffix}"
        if not candidate.exists():
            return candidate
        i += 1


def build_target_name(date_prefix: str, index: int, src: Path) -> str:
    idx = f"{index:03d}"
    clean_stem = sanitize_stem(src.stem, max_len=80)
    return f"{date_prefix}_{idx}_{clean_stem}{src.suffix}"


def list_files_non_recursive(root: Path) -> List[Path]:
    items: List[Path] = []
    for p in root.iterdir():
        if p.is_file():
            items.append(p)
    return sorted(items, key=lambda x: x.name.lower())


def to_markdown_report(root: Path, mode: str, results: List[ItemResult]) -> str:
    total = len(results)
    success = sum(1 for r in results if r.status == "success")
    skipped = sum(1 for r in results if r.status == "skipped")
    failed = sum(1 for r in results if r.status == "failed")

    lines: List[str] = []
    lines.append("# 文件整理报告")
    lines.append("")
    lines.append(f"- 根目录:`{root}`")
    lines.append(f"- 模式:`{mode}`")
    lines.append(f"- 生成时间:`{dt.datetime.now().isoformat(timespec='seconds')}`")
    lines.append("")
    lines.append("## 统计")
    lines.append("")
    lines.append(f"- 总数:{total}")
    lines.append(f"- 成功:{success}")
    lines.append(f"- 跳过:{skipped}")
    lines.append(f"- 失败:{failed}")
    lines.append("")
    lines.append("## 明细")
    lines.append("")
    lines.append("| 原文件名 | 新文件名 | 状态 | 原因 |")
    lines.append("|---|---|---|---|")
    for r in results:
        lines.append(f"| `{r.src_name}` | `{r.dst_name}` | {r.status} | {r.reason} |")
    lines.append("")
    return "\n".join(lines)


def organize(root: Path, mode: str) -> Tuple[List[ItemResult], List[str]]:
    logs: List[str] = []
    results: List[ItemResult] = []

    if not root.exists():
        raise FileNotFoundError(f"root not found: {root}")
    if not root.is_dir():
        raise NotADirectoryError(f"root is not a directory: {root}")

    files = list_files_non_recursive(root)
    date_prefix = dt.datetime.now().strftime("%Y%m%d")

    logs.append(f"[INFO] found {len(files)} file(s) in {root}")

    index = 1
    for src in files:
        try:
            target_name = build_target_name(date_prefix, index, src)
            dst = root / target_name
            dst = resolve_conflict(dst)

            if src.name == dst.name:
                results.append(ItemResult(src.name, dst.name, "skipped", "same name"))
                logs.append(f"[SKIP] {src.name} -> {dst.name} (same name)")
            else:
                if mode == "apply":
                    os.replace(str(src), str(dst))
                    results.append(ItemResult(src.name, dst.name, "success", "renamed"))
                    logs.append(f"[OK]   {src.name} -> {dst.name}")
                else:
                    results.append(ItemResult(src.name, dst.name, "success", "dry-run planned"))
                    logs.append(f"[PLAN] {src.name} -> {dst.name}")

            index += 1
        except Exception as e:
            results.append(ItemResult(src.name, "", "failed", repr(e)))
            logs.append(f"[ERR]  {src.name} failed: {e!r}")

    return results, logs


def parse_args() -> argparse.Namespace:
    p = argparse.ArgumentParser(description="Batch rename files and generate a report.")
    p.add_argument("--root", required=True, help="Target directory to organize (non-recursive).")
    p.add_argument("--mode", choices=["dry-run", "apply"], default="dry-run", help="dry-run or apply.")
    p.add_argument("--report", default="report.md", help="Report output path (Markdown).")
    return p.parse_args()


def main() -> None:
    args = parse_args()
    root = Path(args.root).expanduser()

    results, logs = organize(root=root, mode=args.mode)

    report_path = Path(args.report).expanduser()
    report_text = to_markdown_report(root=root, mode=args.mode, results=results)
    report_path.write_text(report_text, encoding="utf-8")

    for line in logs:
        print(line)
    print(f"[INFO] report written to: {report_path.resolve()}")


if __name__ == "__main__":
    main()

5. 运行与检查(8 分钟)

第一次:dry-run 预演(不改文件)

bash 复制代码
python file_organizer.py --root "D:\atomcode-file-organizer\samples\raw" --mode dry-run --report "D:\atomcode-file-organizer\report.md"

检查点:

  • 终端输出是否出现 [PLAN] ... -> ...
  • report.md 是否生成
  • 报告"明细表格"里是否每行都有原名、新名、状态、原因

第二次:apply 执行(真正改名)

确认预演无误后执行:

bash 复制代码
python file_organizer.py --root "D:\atomcode-file-organizer\samples\raw" --mode apply --report "D:\atomcode-file-organizer\report.md"

检查点:

  • 目录里文件名是否已变为 YYYYMMDD_001_... 的形式
  • 冲突文件是否自动追加 -1/-2
  • 报告统计(成功/跳过/失败)是否符合实际

6. 新手常见问题(以及如何让 AtomCode 解决)

6.1 最容易翻车:直接 apply

建议养成固定流程:

  1. dry-run 生成计划与报告
  2. 你确认映射没问题
  3. apply

你也可以让 AtomCode 增强安全性,例如新增 --confirm yes 才允许执行 apply

6.2 清理规则不明确会导致"看似能用,实际混乱"

文件名清理涉及大量边界情况(空格、特殊字符、全是符号、超长文件名)。本文把规则写进代码里,并且:

  • 为空时兜底为 unnamed
  • 连续 _ 合并
  • 统一截断到 80 字符

6.3 冲突处理要可预期

真实目录里经常出现重复文件名或相近文件名。冲突策略要稳定且可解释:本文通过 -1/-2/... 处理,报告也会记录最终名称,方便追溯。


7. 下一步怎么练(可选拓展)

当你跑通本教程后,推荐用 AtomCode 再做 1 个小迭代(保持增量修改):

  • 增加 --recursive:递归整理子目录
  • 增加 --csv:输出 CSV 报告便于 Excel 查看
  • 增加 --rollback-script:生成回滚脚本,避免误操作无法恢复

结语

用 AtomCode 学习上手的关键不是"让它写更多代码",而是:

  • 把需求约束讲清楚
  • dry-run 做可验证闭环
  • 小步增量迭代

只要你能把"规则"和"验证方式"写出来,AtomCode 就能稳定地帮你把想法落地为可运行代码。

相关推荐
郝学胜-神的一滴2 小时前
力扣 662 :二叉树最大宽度
java·数据结构·c++·python·算法·leetcode·职场和发展
2301_764441332 小时前
基于Stackelberg博弈的分散式库存模型
python·算法·数学建模
GitCode官方2 小时前
AtomGit 5月:下载中心上线;AtomCode Air 新品发布会顺利开展;AtomGit AI 荣获「昇腾开源合作杰出团队奖」
人工智能·开源·atomgit
是Dream呀2 小时前
通道注意力机制|Channel Attention Neural Network
人工智能·python·深度学习
yaoxin5211232 小时前
430. Java 日期时间 API - 时间计算 Temporal 包
java·前端·python
m0_737302582 小时前
OpenClaw:让AI从对话走向落地的开源智能体新范式
人工智能·开源
RSTJ_16253 小时前
PYTHON+AI LLM DAY SEVENTY
人工智能·python·深度学习
财经资讯数据_灵砚智能3 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(日间)2026年6月8日
大数据·人工智能·python·ai·信息可视化·自然语言处理·灵砚智能
隔窗听雨眠3 小时前
硬件巡检自动化:图吧工具箱命令行接口与脚本集成实践
自动化脚本·图吧工具箱