C++17 filesystem 实战:路径拼接、目录遍历和错误处理
手工拼路径字符串容易混淆斜杠、目录和文件名。C++17 的 std::filesystem 提供 path 与一组文件系统操作,让代码直接表达“路径”“文件大小”“遍历目录”等意图。
最低标准:C++17。下面示例只读取当前目录,不创建或删除文件。
1. 用 path 拼接路径
#include<filesystem>#include<iostream>#include<system_error>intmain(){namespacefs=std::filesystem;fs::path file=fs::path("logs")/"app.log";std::cout<<file.generic_string()<<'\n';std::cout<<file.filename().string()<<'\n';std::cout<<file.extension().string()<<'\n';std::error_code ec;fs::directory_iteratorit(".",ec),end;if(ec){std::cerr<<ec.message()<<'\n';return1;}intshown=0;while(it!=end&&shown<3){std::cout<<it->path().filename().string()<<'\n';++shown;it.increment(ec);if(ec){std::cerr<<ec.message()<<'\n';return1;}}}前三行分别是 logs/app.log、app.log、.log;后面的目录项随运行位置而变化,遍历顺序不保证固定。需要稳定展示时,应先收集路径再排序。
2. error_code 版本也要认真检查
很多 filesystem 函数同时提供抛异常版本和接收 std::error_code 的版本。批量扫描文件时,后者方便记录错误后继续处理。不能只检查迭代器构造:遍历过程中权限或目录状态变化,同样可能让 increment 失败。
error_code 重载主要改变文件系统错误的报告方式,不代表程序从此不可能因内存分配等原因抛异常。是否继续处理、跳过目录还是终止,应由业务策略决定。
3. exists 不是后续操作的保证
“先 exists,再打开”之间,文件可能已经被其他进程移动或删除。应始终检查真正的打开、读取或写入结果;exists 只能描述检查瞬间的状态,不能充当安全保证。
同理,file_size 需要适用的文件对象并可能失败。对目录、权限不足的路径或不存在的文件,不要把返回结果当成正常大小直接使用。
4. 路径规范化和安全边界
lexically_normal 进行词法整理,不访问文件系统;canonical 会解析实际存在的路径并处理符号链接,两者不能混用。若用户输入路径,简单拼接或消去“…”不足以保证它位于指定目录内,符号链接与并发变化也需要考虑。
跨平台时,路径字符串编码与大小写规则也可能不同。接口内部尽量保持 path 类型,只有与界面、协议或外部库交互时才做明确的编码转换。
速记:用 path 拼接;用实际操作结果判断成功;遍历和错误都按发生时处理;不要把路径字符串看成可靠的访问权限边界。