2026-10-06 · 本脚本已实跑验证(10 个脏文件全部正确归位,重名不覆盖,重跑幂等)
下载文件夹用半年是什么样:photo (1).jpg、合同 最终版.docx、工资表.xlsx、存档.zip 混在一起,翻找全靠搜索。Windows 11 有个"按类型分组"但只是视图不搬文件;这里用纯标准库 Python(shutil + os,零依赖)真正把文件移进 图片/ 文档/ 表格/ PDF/ 压缩包/ 子目录。
# -*- coding: utf-8 -*-
"""文件夹按扩展名自动分类:图片/文档/表格/PDF/压缩包/其他。"""
import os, shutil, sys
RULES = {
"图片": {".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".svg"},
"文档": {".doc", ".docx", ".txt", ".md", ".rtf"},
"表格": {".xls", ".xlsx", ".csv"},
"PDF": {".pdf"},
"压缩包": {".zip", ".rar", ".7z", ".tar", ".gz"},
"PPT": {".ppt", ".pptx"},
}
def classify(ext):
for cat, exts in RULES.items():
if ext in exts:
return cat
return "其他"
def main(folder):
moved, report = 0, []
for root, dirs, files in os.walk(folder):
if root != folder:
continue # 只处理顶层,已分类的子目录不再翻
for fn in sorted(files):
ext = os.path.splitext(fn)[1].lower()
cat = classify(ext)
cat_dir = os.path.join(folder, cat)
os.makedirs(cat_dir, exist_ok=True)
dst = os.path.join(cat_dir, fn)
if os.path.exists(dst): # 重名防覆盖:加后缀
base, e = os.path.splitext(fn)
i = 1
while os.path.exists(dst):
dst = os.path.join(cat_dir, f"{base}_{i}{e}")
i += 1
report.append(f"[重名] {fn} -> {os.path.basename(dst)}")
if os.path.abspath(dst) != os.path.abspath(os.path.join(folder, fn)):
shutil.move(os.path.join(folder, fn), dst)
moved += 1
report.append(f"[OK] {fn} -> {cat}/")
print(f"=== 分类完成,共移动 {moved} 个文件 ===")
print("\n".join(report))
if __name__ == "__main__":
main(sys.argv[1])
测试数据故意选了最难伺候的一批:photo (1).jpg(空格+括号)、合同 最终版.docx(中文+空格)、截图 2026-10-06.png、无扩展名文件(没有扩展名)、data.csv 等 10 个。
=== 分类完成,共移动 10 个文件 ===
[OK] data.csv -> 表格/
[OK] notes.md -> 文档/
[OK] photo (1).jpg -> 图片/
[OK] readme.txt -> 文档/
[OK] 合同 最终版.docx -> 文档/
[OK] 存档.zip -> 压缩包/
[OK] 工资表.xlsx -> 表格/
[OK] 截图 2026-10-06.png -> 图片/
[OK] 报告.pdf -> PDF/
[OK] 无扩展名文件 -> 其他/
ls -R 复核:6 个子目录,10 个文件各归各位,一个不少。
故意在顶层放 a.txt,同时 文档/ 里已有旧的 a.txt:
[重名] a.txt -> a_1.txt
[OK] a.txt -> 文档/
文档/: a.txt a_1.txt # 两个都在,旧文件未被覆盖
如果这里图省事直接 shutil.move 不检查,旧 a.txt 会被静默覆盖——新文件是下载的还行,万一旧文件是你改了三天的文档,就是数据事故。这是本脚本最重要的一行。
对已整理好的目录再跑一遍:共移动 0 个文件,无报错无副作用。随手双击重跑也不怕。
os.walk 不限顶层 = 整理完又翻出来第一直觉是 os.walk(folder) 全遍历,但分类子目录建好后 walk 会走到 图片/、文档/ 里去,把刚归好位的文件再分类一遍(文档/ 里的 .txt 又会被"移到" 文档/,好在这步因路径相同被跳过;但嵌套目录场景就会出现二次搬家和 图片/图片/ 嵌套)。必须 if root != folder: continue 只处理顶层。
shutil.move 在目标已存在时的行为是覆盖(同文件系统)。下载的 a.txt 覆盖掉文档里的 a.txt,没有任何提示。解法:目标存在就循环找 a_1.txt、a_2.txt,并把改名决定写进报告让用户事后可查。
os.path.splitext("无扩展名文件") 返回 ("无扩展名文件", ""),空扩展名匹配不到任何规则,归入"其他/"兜底——比报错中断整批、或丢在原地假装无事发生都好。想更智能可以再用 file 命令 / python-magic 嗅探内容,但对下载目录场景扩展名兜底已够用。
.JPG、Pdf.PDF 这种大写扩展名很常见,比较前必须 .lower(),否则全进"其他/"。
文件整理、批量改名、重复文件清理这些桌面杂活,喵窝都提供脚本定制服务(¥9.9 起):说出你的文件夹乱法和想要的结果,交付可复用脚本 + 使用说明,包免费修复(虚拟物品交付后不退款;未达承诺免费重做)。
→ 去喵窝资源铺看服务