Windows文件名非法字符包括<、>、:、"、/、、|、?、*共9个ASCII字符,以及CON、PRN、AUX、NUL、COM1–COM9、LPT1–LPT9等不区分大小写的保留设备名。

Windows 文件名非法字符有哪些
Windows 系统禁止在文件名中使用 <、>、:、"、/、、|、?、* 这 9 个 ASCII 字符,无论出现在名字中间、开头或结尾都不行。另外,保留设备名如 CON、PRN、AUX、NUL 及 COM1–COM9、LPT1–LPT9(不区分大小写)也不能作为纯文件名或目录名——哪怕加了扩展名,比如 CON.txt 仍会被系统拒绝创建。
如何用 C++ 检查字符串是否合法
标准库没提供现成函数,得自己写逻辑。核心是两步:检查非法字符 + 检查保留名。注意:std::string 或 std::wstring 都要支持,但 Windows API 多用宽字符,建议优先用 std::wstring 避免编码歧义。
- 遍历每个字符,用
std::find_first_of查找非法字符集,如发现任意一个就返回 false - 提取主文件名(不含扩展名),转为大写后比对保留名列表;可用
std::wstring_convert(C++17 前)或std::toupper配合 locale 转换,但更稳妥的是手动映射:对每个wchar_t调用towupper - 空字符串、仅空格、以点号或空格结尾(如
"file. ")也非法,需额外 trim 和校验
示例片段(简化版):
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
bool is_valid_windows_filename(const std::wstring& name) {
if (name.empty()) return false;
static const std::wstring illegal_chars = L"<>:"/\|?*";
if (name.find_first_of(illegal_chars) != std::wstring::npos) return false;
size_t dot_pos = name.find_last_of(L'.');
std::wstring stem = (dot_pos == std::wstring::npos) ? name : name.substr(0, dot_pos);
// trim trailing spaces and dots
size_t end = stem.find_last_not_of(L" .");
if (end == std::wstring::npos) return false;
stem = stem.substr(0, end + 1);
if (stem.empty()) return false;
// check reserved names (case-insensitive)
static const std::vector<std::wstring> reserved = {L"CON", L"PRN", L"AUX", L"NUL"};
std::wstring upper_stem = stem;
std::transform(upper_stem.begin(), upper_stem.end(), upper_stem.begin(), towupper);
if (std::find(reserved.begin(), reserved.end(), upper_stem) != reserved.end()) return false;
return true;
}
为什么不能只靠 CreateFileW 返回值判断
很多人想跳过校验,直接调 CreateFileW 看是否失败。这不可靠:它可能因权限不足、路径不存在、磁盘满等非命名问题报错,错误码是 ERROR_INVALID_NAME 才表示命名违规,但该错误只在极少数情况下触发(比如含非法字符),而保留名冲突往往返回 ERROR_ACCESS_DENIED 或静默失败(如 CON.txt 创建时看似成功,实则写入无效)。所以必须前置校验,不能依赖 API 反馈。
立即学习“C++免费学习笔记(深入)”;
容易被忽略的边界情况
Windows 对路径总长也有约束(MAX_PATH 默认 260),但这是路径层级限制,不是单个文件名规则;真正易漏的是:. 和 .. 作为文件名非法(哪怕加扩展名),file. 结尾的点号会被系统自动截掉,导致重命名意外;还有 Unicode 控制字符(如 U+202E RTL override)虽不属“非法字符”,但可能引发安全混淆,生产环境建议一并过滤。

















