c++oding="utf-8" ?>
跨平台文件名校验需同时满足posix和windows约束,仅用std::filesystem::portable_posix_name()不够;必须组合调用portable_posix_name()和windows_name(),并额外处理保留名及系统级写入失败。

没有“严格符合POSIX跨平台标准规范”这回事。 POSIX 本身不定义跨平台规则,Windows 也不遵循 POSIX;所谓“跨平台安全文件名”,其实是取 POSIX 和 Windows 两套约束的交集——必须同时满足两者,缺一不可。
std::filesystem::portable_posix_name() 是什么、不是什么
它是一个 C++17 函数,用于校验单个文件名是否属于主流 libc++/libstdc++ 实现采纳的 POSIX 兼容子集。它检查:
- 长度 ≤ 255
字节(不是字符数,UTF-8 中文或 emoji 可能占 3–4 字节) - 不含
/、\0、ASCII 控制字符(\x00–\x1F和\x7F) - 不以
.或..开头(.config合法,..hidden不合法) - 允许 UTF-8 多字节字符(如中文、emoji),因为它是按字节处理,不调用
isalnum()等字符函数
但它不检查::、*、?、"、、<code>>、| 等 Windows 非法字符;也不拦截 CON、COM1 等保留名;更不验证路径是否存在或能否写入。
传入 const char* 会编译失败,必须是 std::string 或可隐式转为 std::filesystem::path 的类型。
为什么不能只用 portable_posix_name() 做跨平台校验
这个函数在 Windows 下完全不生效:它对 a:b.txt 返回 true(Linux 合法),但 Windows 的 CreateFile 会直接失败;对 CON.txt 也返回 true(POSIX 不管设备名),而 Windows 拒绝创建。
常见错误现象:
-
std::filesystem::portable_posix_name("中文.txt")→true(正确) -
std::filesystem::portable_posix_name("a/b/c.txt")→false(含/,它只校验“名字”,不是“路径”) -
std::filesystem::portable_posix_name("-config.json")→false(它比 POSIX 更严,禁止以-开头) -
std::filesystem::portable_posix_name("foo\xC0\x80")→true(非法 UTF-8 序列被放过,但open()会因EINVAL失败)
跨平台文件名校验的正确组合方式
真正安全的做法是两个函数都调用,且顺序不能颠倒:
bool is_cross_platform_filename(const std::string& s) {
return std::filesystem::portable_posix_name(s) && std::filesystem::windows_name(s);
}
std::filesystem::portable_posix_name(s) 先快速筛掉含 /、\0、控制符、超长或以 . 开头的字符串;std::filesystem::windows_name(s) 再覆盖 Windows 特有禁忌(9 个非法字符、ASCII 控制符、结尾空格/点)。
注意:std::filesystem::windows_name() 在非 Windows 平台可能未实现或返回 false,所以该组合逻辑应仅在明确支持双平台的构建中启用;若目标平台仅为 Linux/macOS,加 windows_name() 反而会误杀合法名。
保留名(如 CON)需额外处理:std::filesystem::windows_name("CON.txt") 返回 true(它只在校验无扩展名时触发),必须配合类似 is_valid_win_filename() 的自定义函数补漏。
最容易被忽略的是:所有这些校验都只回答“能不能当文件名用”,不回答“能不能成功写入”。非法 UTF-8、NFS 挂载限制、权限不足、磁盘满等问题,只能靠系统调用后的 errno 判断,不是名字校验能覆盖的。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











