C 盘飘红的时候,大部分人的处理流程是这样的:右键属性 → 磁盘清理 → 勾上所有能勾的 → 清理出 2 GB → 三天后又红了。
问题不在于清理工具不够狠,而在于你根本不知道那 200 GB 到底在哪儿。Windows 自带的磁盘清理只认识它自己定义的那十几类文件,而真正吃掉空间的往往是:某个视频软件攒了 8 GB 的缓存、驱动更新工具留下的 5 GB 安装包、三年前装的项目里的 node_modules、以及一个你早就忘了的 60 GB 虚拟机磁盘。
DiskSniffer 就是为这件具体的事写的:看清空间去向 → 分清轻重 → 安全地删掉。

它做什么
交互思路参考了经典的 SpaceSniffer——方块面积等于占用大小,一眼看出谁是大头。但重点不同:SpaceSniffer 只负责「看」,DiskSniffer 要回答的是「然后呢,我该删哪个」。
扫描完成后是五个页签:
| 页签 | 用途 |
|---|---|
| 空间树图 | 方块面积 = 占用大小,颜色 = 重要程度。双击方块下钻,右键返回上层 |
| 大文件 | 单个大文件排行(含完整路径),C 盘紧张时这里一删就见效 |
| 清理建议 | 勾选后一键删到回收站。只有「可安全清理」会被一键全选 |
| 系统级大户 | 休眠文件 / 虚拟内存 / 还原点 / WinSxS —— 这些不能直接删,给出官方命令 |
| 报告 | 完整文本报告,可导出 TXT / CSV / JSON |
左边始终是按占用降序的目录树,每一行都标了级别和理由,不是让你对着一堆路径自己猜。
四种颜色,四种态度
这是整个工具的核心。内置规则库把每一条路径判成四级,颜色和工具的行为是绑死的:
| 级别 | 含义 | 工具的行为 |
|---|---|---|
| 🟩 可安全清理 | 缓存、临时文件、日志、崩溃转储、更新残留 | 可被「一键全选」,删了系统会自己重建 |
| 🟨 需确认 | 下载、虚拟磁盘、node_modules、安装包、压缩包 | 出现在候选列表里,但必须你逐条勾 |
| 🟦 重要数据 | 文档、图片、配置、SSH 密钥 | 不会出现在清理候选里 |
| 🟥 系统关键 | System32、WinSxS、Program Files、引导分区 | 不会出现在候选里,删除接口也硬性拒绝 |
规则的匹配顺序是「路径优先,其次通用名,最后兜底」:精确路径规则(如 %LOCALAPPDATA%\Temp)> 通用目录名(任意位置的 Cache、Crashpad、__pycache__)> 扩展名(.dmp、.tmp、.iso)> 启发式兜底。谁更具体谁赢,所以 %LOCALAPPDATA%\Temp(可清理)不会被上层的 %USERPROFILE%(重要数据)盖住。

三个真正花了心思的地方
一、「忽略小文件」不等于「不统计小文件」
需求里最容易做错的一条。你设了「忽略小于 100 MB 的条目」,如果实现成「跳过不统计」,那么一个装了两万个 3 KB 文件的 node_modules 就会显示成 0 —— 而它恰恰是你要找的东西。
正确做法是折叠而不是丢弃:低于阈值的条目照常计入父目录的总大小,只是不单独建节点,界面上显示成一条「其他 N 项小条目」。
自检里有一条专门逐字节验证它:阈值从 0 调到 100 MB,两次扫描的根目录总大小必须一字节不差。
二、WinSxS 显示的大小是虚的,而且必须说出来
C:\Windows\WinSxS 里绝大多数文件是 System32 里同一份数据的硬链接。按文件大小统计会把同一块数据重复计算,所以它常被算成 8–15 GB,而实际独占的空间往往只有 1–3 GB。
做硬链接去重需要读取每个文件的 FileIndex,代价极高,所以工具选择不做——但必须在界面上把这件事说清楚。因为如果不说,用户看到「WinSxS 占了 12 GB」的第一反应就是去删它,而手动删 WinSxS 会毁掉系统更新能力,严重时无法开机。
所以 WinSxS 在这个工具里没有任何删除入口,只给官方分析命令:
Dism /Online /Cleanup-Image /AnalyzeComponentStore
三、目录联结(junction)一律跳过
C:\Users\All Users → C:\ProgramData、C:\Documents and Settings → C:\Users,这类目录联结如果跟进去,会让 C 盘统计出比实际大得多的数字;成环时递归还永远不会结束。
判断方式不用 os.path.islink(它对 junction 的行为在不同 Python 版本上不一致),而是直接查 Windows 文件属性位 FILE_ATTRIBUTE_REPARSE_POINT——这是系统给出的权威答案。状态栏里那句「跳过 N 个链接/联结」就是这些。
安全设计:三道闸门,前两道是结构性的
删除是不可逆操作,所以这部分不能靠「记得别删」,得靠机制:
第一道,候选生成阶段就把危险项排除在外。 清理面板里的候选不是「扫描树里所有节点」,而是由一个专门的函数产出,这个函数在结构上只可能产出「可安全清理」和「需确认」两级:
if verdict.level not in (Level.SAFE, Level.REVIEW):
continue # 系统关键与用户数据根本进不了候选列表
第二道,删除接口无条件拦截保护路径。 不管调用方传了什么进来,System32 / Program Files / 盘符根目录 / 文档目录 / pagefile.sys 一律拒绝。
第三道,默认进回收站。 用 SHFileOperationW + FOF_ALLOWUNDO,删错了可以还原。永久删除是单独的按钮,需要两次确认。
自检里有四条用例(T-17 / T-18 / T-20 / T-21)主动构造越权删除尝试,断言它们被拒绝——包括真的去删 C:\Windows\System32,然后断言目录完好。只测正常路径的测试是没有意义的。
实测数据
不给「据说很快」这种话,给具体数字(均为普通权限运行、NVMe 硬盘):
- 全盘扫描:207 万个文件 / 45 万个目录 / 846 GB,耗时 56.6 秒
- 用户目录:
AppData\Local,64 万文件 / 195 GB,耗时 8–17 秒,找出 24 GB 可安全清理项 - 准确性:逐条核对全盘扫描给出的 24 条「可安全清理」建议,零误报——全部确实是缓存、日志、临时文件或更新残留。同一次扫描里,173 GB 的文档目录和 157 GB 的游戏安装目录都正确地没有进入清理候选
- 自检:31 项全部通过,且全部跑在真实文件系统上(临时目录里真建、真删、真回收站),源码运行和打包后的 EXE 各验证一次
有一个数字值得单独说:那次全盘扫描里有 331 个目录报「拒绝访问」(System Volume Information、其他用户的目录等)。工具会把这个数字单列出来,而不是静默吞掉——你至少要知道有一部分空间没被算进去。
一条踩坑记录:兜底规则差点毁掉整个功能
第一次拿 AppData\Local 做真实扫描时,「可安全清理」只找出 4.57 GB。但同一份结果里,肉眼就能看到某视频软件的缓存目录有 8.5 GB。
查下来是这样:AppData\Local\SynologyDrive(109 GB 的同步视频)被一条兜底规则判成了「需确认 + 可删」,而候选收集器命中一个目录后就不再往下钻(这是为了防止父子重复计入可释放总量),于是整棵子树被这条弱证据吞掉了,里面真正该清的缓存一个都没露出来。
修法是把所有兜底判定降级为「仅说明」——弱证据不产生「可删」结论,也不挡住下面的强证据。同一目录的可清理量从 4.57 GB 涨到 23.37 GB,且每条都有具体理由。
这条教训比修复本身更值钱:兜底规则的作用是「给个说法」,不是「下结论」。
它做不到什么
技术文章最没用的部分是只讲优点,所以这段说实话:
- 不是 WizTree。 WizTree 直读 NTFS 的 MFT,几秒扫完全盘;本工具走
os.scandir多线程,846 GB 要一分钟。换来的是不需要管理员权限读裸设备、也不容易被杀毒软件盯上。机械硬盘上会明显更慢,这个没有实测数据。 - 不做硬链接去重,WinSxS 之类的目录显示值偏大(已在界面上说明原因)。
- 不自动清理。 没有「启动即清理」模式,任何删除都要你亲手点。
- 不碰注册表、不改系统设置、不停服务。 需要这些的项目(休眠文件、虚拟内存、还原点)只给官方命令文本,让你自己执行。
- 完全离线。 不联网、不上报、不检查更新、不读取任何与磁盘占用无关的信息。
还有几项尚未验证的,也一并列出:EXE 只在开发机上试运行过,没在完全没装 Python 的干净机器上验证;上面的实测都是普通权限,提权后能多扫到多少没测过;删除逻辑在临时目录里验证过,但没有真的对几十 GB 的实际缓存执行过一次批量清理;PyInstaller 产物有被杀软误报的可能(已做的规避:不加壳、不用 UPX、写入完整版本信息),但没向任何厂商提交过白名单。
怎么用
方式一:双击 EXE。 单文件 8.1 MB,不需要装 Python。选「分析目标」(默认 C:\),点「开始扫描」。
建议以管理员身份运行,否则 System Volume Information、其他用户的目录会扫不到。界面右下角会提示,并提供一键提权重启。
方式二:命令行。 同一个 EXE,带参数时自动接管控制台输出:
DiskSniffer.exe --scan C:\ --min-size 100MB --special
DiskSniffer.exe --scan C:\Users --json 报告.json --csv 报告.csv
DiskSniffer.exe --selftest
方式三:源码运行。 Python 3.8+,零第三方依赖(tkinter 随 Python 自带)。
技术选型说明
用 Python + tkinter 而不是 C 或 Qt,理由很实际:这个程序的瓶颈是磁盘 IO 和文件系统元数据查询,不是 CPU,C 语言带来的收益远小于开发和维护成本;tkinter 随 Python 自带,打包后不需要额外运行时,换成 Qt 会让 EXE 体积翻三倍。
代价也说清楚:WizTree 那种「直读 MFT,几秒扫全盘」的速度做不到,量级是分钟级而不是秒级。
代码上纯逻辑(app/core)和界面(app/ui)是分开的,核心逻辑不依赖 tkinter,所以 31 项自检可以在命令行里跑,不用起 GUI。
DiskSniffer/
├── main.py 入口(GUI / CLI / 自检 三种模式)
├── app/
│ ├── core/ 纯逻辑,不依赖 tkinter
│ │ ├── model.py Node 数据结构与大小格式化
│ │ ├── scanner.py 多线程扫描
│ │ ├── rules.py 分类规则库 + 清理候选生成(安全约束所在)
│ │ ├── cleaner.py 回收站/永久删除 + 保护路径闸门
│ │ ├── sysinfo.py 磁盘容量、系统级空间大户
│ │ └── report.py TXT / CSV / JSON 导出
│ ├── ui/
│ │ ├── app_window.py 主窗口
│ │ ├── treemap.py squarified 树图
│ │ └── theme.py 配色与 ttk 样式
│ └── selftest.py 31 项自检
└── docs/ 设计文档与迭代记录
下一版计划
已经在做的几项改进,都来自实际使用中的反馈:
- 和文件资源管理器打通——树图路径栏加「在资源管理器中打开」按钮,大文件列表支持右键直接定位到文件。找到了却还要自己复制路径去粘贴,这个断层很别扭。
- 修长路径显示不全——长文件名在列表里被截断,滚动条也不够跟手。
- 加快永久删除——目前删几十万个小文件时明显偏慢。一部分是物理必然(进回收站只是移动目录项,永久删除要真的释放每一条 MFT 记录),但一部分可以优化:批量路径一次性提交给 Shell、对整棵目录树直接删目录而不是遍历每个文件。顺带说一句,杀毒软件的实时防护会 hook 每一次文件删除,批量清理时它往往才是真正的瓶颈——这个工具侧优化不掉,只能给缓存目录加排除项。
- 消除拖动窗口时的残影——这是重绘优化问题,不是显卡问题。典型成因是窗口尺寸变化事件直接触发树图全量重算,拖动时每秒几十次。加上事件合并(debounce)和布局缓存就能解决。
结语
写这个工具时最大的体会是:磁盘清理类工具最难的不是「找到大文件」,而是「敢不敢让用户点删除」。