std::filesystem::canonical 返回绝对、无符号链接、无.或..的路径,但要求路径存在且可访问;若中间目录不存在或权限不足,则抛出filesystem_error。

canonical 会解析符号链接但不处理相对路径中的 ..
std::filesystem::canonical 的核心行为是:从给定路径出发,按实际文件系统结构向上追溯,直到得到一个**绝对、无符号链接、无 . 或 .. 的路径**。但它不会在解析前先“规整”相对路径——比如传入 "./../a/b",它会先尝试以当前工作目录为起点拼接这个路径,再逐级解析。如果中间某级不存在或权限不足,就会抛出 std::filesystem::filesystem_error。
常见错误现象:canonical("foo/../bar") 在当前目录下没有 foo 目录时直接失败,而不是先简化成 "bar" 再查。
- 必须确保路径所指的**最终目标存在且可访问**(包括所有中间目录)
- 若只想做纯文本规整(不查磁盘),用
std::filesystem::weakly_canonical更安全 - Windows 下注意路径分隔符:传入
"C:\foo\..\bar"没问题,但"C:/foo/../bar"同样被接受(std::filesystem自动标准化分隔符)
需要捕获 filesystem_error 来避免程序崩溃
只要路径不存在、权限不够、跨挂载点(某些系统)或遇到循环软链,canonical 就会抛异常。不捕获就等于把程序交给操作系统信号处理——通常就是终止。
示例:
立即学习“C++免费学习笔记(深入)”;
try {
auto p = std::filesystem::canonical("nonexistent/path");
std::cout << p << "
";
} catch (const std::filesystem::filesystem_error& e) {
// e.what() 包含具体原因,如 "No such file or directory"
std::cerr << "resolve failed: " << e.what() << "
";
}
- 不要假设
canonical总是成功;生产代码中几乎必须包裹try/catch -
e.code().value()可提取 errno(如 Linux 上 2 表示 ENOENT),便于分类处理 - 若想静默失败,可先用
exists(p)或is_symlink(p)做预检,但无法覆盖所有canonical可能失败的情形
canonical 和 weakly_canonical 的关键区别
weakly_canonical 不要求最终路径存在,只保证前缀部分被规范化。比如当前目录是 /home/user,调用 weakly_canonical("foo/../bar") 会返回 /home/user/bar;而 canonical("foo/../bar") 会因 foo 不存在直接失败。
- 当你只需要“把相对路径转成绝对路径并展开软链前缀”,用
weakly_canonical - 当你明确需要确认路径真实可达并获取其唯一物理位置(例如日志归档、配置校验),才用
canonical - 两者都支持指定 base 参数:
canonical("a/b", "/base")等价于canonical("/base/a/b")
跨平台路径拼接时别依赖 current_path()
很多人写 canonical("data/config.json"),指望它基于项目根目录解析——但它的起点是 std::filesystem::current_path(),而该值可能被其他代码或 shell 环境修改过,不可控。
- 显式传入基路径更可靠:
canonical("data/config.json", project_root) -
project_root可通过argv[0]解析可执行文件所在目录获得,而非依赖当前工作目录 - macOS/Linux 下注意:挂载点(如 /Volumes/xxx)可能导致
canonical返回意外路径,尤其涉及硬链接或 APFS 克隆时
canonical 的那一刻,文件系统状态已经变了——目标可能刚被删除,或者权限临时变更。别把它当纯函数用,得按 I/O 操作来设计容错。


















