Path.GetFileNameWithoutExtension是.NET内置最稳妥的方式,专为提取文件名(不含扩展名)设计,能正确处理各种路径格式及边界情况,如archive.tar.gz、.gitignore等,且跨平台行为一致。

用 Path.GetFileNameWithoutExtension 最直接
这是 .NET 内置最稳妥的方式,专为这个需求设计,能正确处理各种路径格式(带盘符、UNC、相对路径、末尾斜杠等)。它内部已规避了手动切字符串可能引发的边界问题。
常见错误是用 string.Split('.')[-1] 或正则匹配最后一段,结果在 archive.tar.gz、.gitignore、file. 这类文件上出错——前者取到 gz,后者抛异常或返回空。
-
Path.GetFileNameWithoutExtension(@"C:\data\report.txt")→"report" -
Path.GetFileNameWithoutExtension(@"./logs/app.log.bak")→"app.log" -
Path.GetFileNameWithoutExtension(@".editorconfig")→""(符合预期:无主文件名)
注意 Path.GetFileName 和 Path.GetExtension 的配合陷阱
有人试图用 Path.GetFileName(path).Replace(Path.GetExtension(path), ""),逻辑看似合理,但有两处隐患:
- 当文件名以点开头(如
.env),Path.GetExtension返回".env",导致结果为空字符串,而非期望的".env"去掉扩展名后的""(其实这反而是对的,但容易误判) - 如果路径末尾是斜杠(如
"dir/"),Path.GetFileName返回空字符串,再调Replace会抛NullReferenceException - 性能上多一次字符串分配和遍历,不如原生方法一次到位
跨平台路径需留意 Path 的默认分隔符行为
Path.GetFileNameWithoutExtension 在 Windows 和 Linux/macOS 上行为一致,它不依赖当前系统的路径分隔符,而是按语义解析:只认最后一个 / 或 \ 后的部分作为“文件名”,再从中剥离扩展名。
这意味着你传入 "src/Program.cs" 或 "src\Program.cs",结果都是 "Program"。但要注意:如果路径本身不合法(如含控制字符、*、?),该方法不会抛异常,仍会尝试截取——实际使用前建议先用 File.Exists 或 Directory.Exists 验证路径有效性。
需要保留多级扩展名时不能依赖这个 API
像 docker-compose.yml 或 package-lock.json 这类“双扩展名”文件,.NET 默认只剥离最后一个点之后的部分:Path.GetFileNameWithoutExtension("docker-compose.yml") 返回 "docker-compose",而不是 "docker"。没有内置方法能智能识别惯例中的“主扩展名”。
若业务明确要求按约定拆分(例如所有 -compose.yml 视为整体扩展),就得自己写逻辑:
var fileName = Path.GetFileName(path);
var idx = fileName.LastIndexOf("-compose.yml", StringComparison.Ordinal);
if (idx > 0) {
fileName = fileName.Substring(0, idx);
}
这种定制化处理没法泛化,得按项目规范硬编码,也正因如此,Path.GetFileNameWithoutExtension 的“简单剥离最后一个点”设计反而更可靠——它不猜测意图,只做确定的事。










