4. 目录结构快照与差异对比
任务拆解:快照要记录什么
目录结构快照的核心是把“某一时刻的目录状态”序列化为可比较的数据。对每个文件,至少记录三项:相对路径、文件大小、最后修改时间(mtime)。目录本身也记录,但只记路径和类型,不参与“修改”判定。JSON 结构采用扁平字典,键为相对路径,值为对象,便于两次快照做集合运算。
快照 JSON 结构示例:{"src/main.py": {"type": "file", "size": 2048, "mtime": 1718000000.0}, "src/utils": {"type": "dir"}}
生成快照
用 os.walk 自顶向下遍历,os.path.relpath 转相对路径,os.stat 取大小与 mtime。mtime 用 st_mtime 浮点秒,避免格式化带来的精度损失。写入时用 json.dump(..., ensure_ascii=False, indent=2, sort_keys=True),保证同一目录两次生成的文件字节级可比。
import os, json, time
def snapshot(root):
snap = {}
for dirpath, dirnames, filenames in os.walk(root):
for d in dirnames:
p = os.path.join(dirpath, d)
snap[os.path.relpath(p, root)] = {"type": "dir"}
for f in filenames:
p = os.path.join(dirpath, f)
st = os.stat(p)
snap[os.path.relpath(p, root)] = {
"type": "file",
"size": st.st_size,
"mtime": st.st_mtime,
}
return snap
def save_snapshot(root, out_path):
data = {"root": os.path.abspath(root), "time": time.time(), "entries": snapshot(root)}
with open(out_path, "w", encoding="utf-8") as fp:
json.dump(data, fp, ensure_ascii=False, indent=2, sort_keys=True)
return data
差异对比逻辑
把两次快照的 entries 取出,用集合运算得到三类结果:新增 = 新键 − 旧键;删除 = 旧键 − 新键;修改 = 键相同但 size 或 mtime 不同。目录只参与新增/删除,不参与修改判定。
def diff(old, new):
o, n = old["entries"], new["entries"]
added = sorted(set(n) - set(o))
removed = sorted(set(o) - set(n))
modified = []
for k in sorted(set(o) & set(n)):
a, b = o[k], n[k]
if a["type"] != "file" or b["type"] != "file":
continue
if a["size"] != b["size"] or a["mtime"] != b["mtime"]:
modified.append(k)
return {"added": added, "removed": removed, "modified": modified}
结果判读与验证
验证方法:先对目录做一次快照存为 snap_a.json;手动新增一个文件、删除一个文件、修改一个文件内容(触发 mtime 变化);再生成 snap_b.json;运行 diff,检查三个列表是否与手动操作一一对应。若修改列表为空,先确认文件系统 mtime 精度是否足够(部分系统为秒级,快速连续写入可能不触发变化)。
mtime 精度不足时,可改用
st_mtime_ns 纳秒字段,或对文件内容做哈希(如 hashlib.sha1)作为补充判据。可复用检查清单
| 检查项 | 通过条件 |
|---|---|
| 快照键为相对路径 | 换机器/换根目录后仍可对比 |
| JSON 排序输出 | sort_keys=True,同状态两次生成字节一致 |
| mtime 精度 | 快速修改能被检出,否则改用 st_mtime_ns 或哈希 |
| 目录不参与修改 | modified 列表只含 type=file 的条目 |
| 三类结果可解释 | added/removed/modified 与手动操作一一对应 |