应使用 std::filesystem::path 替代手拼路径字符串,其内置平台感知分隔符、自动适配构造拼接,并需配合 exists() 和 is_regular_file() 校验后再读写。

用 std::filesystem::path 替代手拼字符串
硬连 "\" 或 "/" 是跨平台路径 bug 的主要来源。C++17 起,std::filesystem::path 内置平台感知:Windows 下用 \ 分隔,Linux/macOS 用 /,且构造、拼接、遍历全部自动适配。
- 错误写法:
std::string p = "data" + "\" + "config.txt";—— Linux 上直接失效 - 正确写法:
std::filesystem::path p = "data" / "config.txt";(注意/是重载运算符,不是字面斜杠) - 拼接多个层级直接链式调用:
p /= "subdir" /= "file.json"; - 获取原生字符串时用
p.string()(Windows 返回"data\subdir\file.json",Linux 返回"data/subdir/file.json")
读写文件前务必调用 std::filesystem::exists() 和 std::filesystem::is_regular_file()
不同系统对路径“存在但不可访问”的判定逻辑不同。比如 Windows 可能返回 exists()==true 但实际是符号链接指向不存在目标;Linux 上某些挂载点未就绪时也容易误判。不校验就打开,常触发 std::filesystem::filesystem_error 或静默失败。
- 先检查路径是否存在且是普通文件:
if (std::filesystem::exists(p) && std::filesystem::is_regular_file(p)) { /* open */ } - 避免只依赖
open()的返回值判断——fopen()或std::ifstream构造失败时,错误原因可能是权限、路径格式、设备忙等,和“路径不存在”不是一回事 - 若需兼容旧标准(C++14 及以下),可用 Boost.Filesystem,接口几乎一致,但需额外链接
std::filesystem::canonical() 不是万能的,慎用于相对路径或用户输入
这个函数会解析符号链接、展开 ..、补全相对路径为绝对路径,看起来很“规范”,但它在跨平台场景下行为差异明显:
- Windows 上要求目标必须真实存在,否则抛异常;Linux 上部分情况可返回“逻辑路径”而不验证底层
- 用户输入的
"../config.ini"经canonical()后可能变成C:UsersAliceppconfig.ini(Windows)或/home/alice/app/config.ini(Linux),但若当前工作目录不同,结果完全不同 - 更安全的做法是:仅对配置文件、资源根路径等可信路径调用;对用户传入路径,优先用
std::filesystem::weakly_canonical()(C++20)或手动std::filesystem::absolute()+std::filesystem::lexically_normal()
环境变量和运行时路径要区分 PATH 和实际文件路径
Windows 的 %APPDATA%、Linux 的 $XDG_CONFIG_HOME 这类变量返回的是平台原生格式路径,不能直接拼接 "datalog" 或 "/data/log"。
立即学习“C++免费学习笔记(深入)”;
- 获取后立刻转成
std::filesystem::path:auto appdata = std::filesystem::path(std::getenv("APPDATA")); auto log_path = appdata / "myapp" / "log.txt"; - 不要用
std::getenv()直接拼接字符串再塞给fopen(),尤其当环境变量为空时,std::getenv()返回nullptr,导致空指针解引用 - 跨平台项目建议封装一个
get_config_dir()函数,内部按 OS 分支处理,统一返回std::filesystem::path
最易被忽略的是:编译期路径宏(如 #define ASSET_PATH "res/textures/")一旦写死分隔符,就锁死了平台。所有字面路径都应该由 std::filesystem::path 构造,哪怕只是单层。











