判断文件内容相同需先比大小再计算哈希;同步时须校验内容而非仅依赖时间戳,用临时文件+原子重命名保证安全性,并缓存哈希以优化I/O性能。

如何判断两个文件是否“内容相同”而非仅看修改时间
只比对 last_write_time 不可靠:文件可能被重写但内容未变,或通过复制/移动操作触发时间戳更新。真正安全的做法是先比大小,再按需计算哈希(如 xxHash32 或 MD5)。小文件(
关键点:
- Windows 上
std::filesystem::last_write_time返回的是 UTC 时间,若源目标时区不同,直接比较可能误判(建议统一转为file_time_type::clock::to_time_t后比对) - Linux/macOS 的 nanosecond 级精度在 FAT32 文件系统上会退化为 2 秒精度,此时仅靠时间戳不可信
- 不要用
std::filesystem::equivalent()判断文件内容——它只判断路径是否指向同一 inode,和内容无关
用 std::filesystem::recursive_directory_iterator 遍历时如何跳过符号链接和权限拒绝目录
默认迭代器遇到权限不足(如 /proc 下某些子目录)或符号链接会抛出 std::filesystem::filesystem_error,导致整个遍历中断。必须显式捕获并跳过:
for (auto iter = fs::recursive_directory_iterator(src, ec); iter != fs::recursive_directory_iterator(); ++iter) {
if (ec) { ec.clear(); continue; }
if (fs::is_symlink(iter->path())) continue;
// ... 处理文件
}
注意:recursive_directory_iterator 构造时传入的 ec 只捕获构造错误;后续 ++iter 的错误需在循环体内单独检查 ec,否则程序崩溃。
立即学习“C++免费学习笔记(深入)”;
常见坑:
- 没传
fs::directory_options::skip_permission_denied选项,导致ec不自动忽略权限错误 - 在 Windows 上遍历网络共享路径(如
\servershare)时,部分子目录可能返回ERROR_BAD_NETPATH,需额外处理ec.value()
同步逻辑中“仅复制修改过的文件”的核心判断条件
目标文件不存在 → 必须复制;目标存在但源更晚 → 检查内容是否真不同;目标存在且源不更新 → 跳过。完整判断链如下:
if (!fs::exists(dst_path)) {
copy_file(src_path, dst_path);
} else if (fs::last_write_time(src_path) > fs::last_write_time(dst_path)) {
if (!files_content_equal(src_path, dst_path)) {
fs::remove(dst_path);
copy_file(src_path, dst_path);
}
}
注意三点:
- 不要用
fs::copy_file(..., fs::copy_options::update):它只比时间戳,不校验内容,且无法跨文件系统(如 NTFS → exFAT) - 复制前先
fs::remove(dst_path)再copy_file,避免目标文件被占用导致失败(Windows 常见) - 若源是只读文件,
copy_file可能失败,需提前fs::permissions(src_path, fs::perms::owner_read)
如何保证同步过程原子性,避免目标目录出现半截文件
直接覆盖目标文件有风险:复制中途崩溃,旧文件已被删,新文件不完整。正确做法是先写临时文件(同目录下带 .tmp 后缀),校验通过后再 fs::rename 替换:
auto tmp_path = dst_path.string() + ".tmp";
copy_file(src_path, tmp_path);
if (files_content_equal(src_path, tmp_path)) {
fs::rename(tmp_path, dst_path);
} else {
fs::remove(tmp_path);
throw std::runtime_error("copy failed: checksum mismatch");
}
关键约束:
-
fs::rename在同一文件系统内是原子操作;跨分区需 fallback 到 remove+rename,此时无法保证原子性 - 临时文件必须与目标同目录(否则 rename 可能跨设备失败),不能放在
/tmp这类独立挂载点 - Windows 上若目标文件正被其他进程打开(如记事本),
rename会失败,需加 retry 逻辑或提示用户关闭程序
最易被忽略的是:同步大量小文件时,频繁调用 fs::last_write_time 和 files_content_equal 会造成磁盘 I/O 暴增——实际项目中应加内存缓存(如 LRU cache 存最近 1000 个文件的 size+hash),并用线程池控制并发校验数。



















