C++17起推荐用std::filesystem::path::extension()提取扩展名,它自动处理路径标准化和边界识别,返回最末段扩展(如".gz"),需调用.string()转换;对".tar.gz"等需业务逻辑判断,不支持自动识别多级扩展。

用 std::filesystem::path::extension() 直接提取扩展名
这是 C++17 起最标准、最可靠的方式,不需要手动字符串切分或正则匹配。std::filesystem::path 会自动识别合法的扩展名边界(比如处理 .tar.gz 这类多级扩展时,extension() 默认只返回最末段,而 stem() 和 filename() 配合可进一步拆解)。
注意:返回值是 std::filesystem::path 类型,不是 std::string,需调用 .string() 或 .u8string() 转换。
- 若路径为
"report.pdf",p.extension().string()→".pdf" - 若路径为
"archive.tar.gz",默认返回".gz"(不是".tar.gz") - 若路径无扩展名(如
"README"或"./config"),返回空path,.string()为空字符串 - 路径含非法字符或根本不是文件(只是字符串),
extension()仍能工作——它只解析路径字面量,不访问文件系统
想拿完整扩展(如 .tar.gz)得用 replace_filename() + stem()
extension() 的设计本意就是“最后一段点号之后的部分”,所以它不负责识别复合扩展。真要提取 .tar.gz,得靠人工判断或额外逻辑,常见做法是先去掉主扩展,再对剩余部分重复试探:
std::filesystem::path p = "data.tar.gz";
std::string base = p.stem().string(); // "data.tar"
std::string ext1 = p.extension().string(); // ".gz"
if (ext1 == ".gz" || ext1 == ".bz2" || ext1 == ".xz") {
auto prev = std::filesystem::path(base).extension();
if (prev == ".tar") {
ext1 = ".tar" + ext1; // ".tar.gz"
}
}
这种逻辑必须基于你明确知道的归档组合列表,没有通用银弹。别指望 filesystem 自动识别所有多级扩展。
立即学习“C++免费学习笔记(深入)”;
Windows 下路径带盘符或反斜杠不影响 extension()
std::filesystem::path 内部会标准化分隔符,无论输入是 "C:\\dir\\file.txt"、"C:/dir/file.txt" 还是 "/home/user/file.txt",extension() 行为完全一致。
- 传入
"C:\\log\\app.v2.log"→ 返回".log" - 传入
"D:/src/CMakeLists.txt"→ 返回".txt" - 但注意:如果路径以点开头且无其他点(如
".gitignore"),extension()会返回".gitignore"(整串当扩展),因为stem()为空 —— 这是符合 POSIX 路径语义的设计,不是 bug
不用 filesystem 时,手撕字符串容易出错
有人用 find_last_of('.') + substr(),但这在以下情况全军覆没:
- 路径含目录名带点:如
"./v1.2.3/build/app.exe"→ 错误截出".3/build/app.exe" - 文件名以点开头:如
".env"→ 应该是".env",但 naive 实现可能返回空或错截 - 路径含查询参数或哈希(虽少见):如
"script.js?v=1.0.2"→ 点在 URL 部分,不该算扩展 - 跨平台换行或宽字符未处理:
std::string方案对 UTF-8 路径基本可用,但遇到 Windows 的wchar_t路径就失效
除非项目卡在 C++11/C++14 且无法引入 filesystem(此时建议用 Boost.Filesystem),否则别自己解析路径字符串。
真正麻烦的是复合扩展和隐藏文件的语义区分,这些不在 filesystem 职责范围内,得由业务层定义规则。









