.NET如何获取规范文件路径以判断路径指向同一文件?
解决方案:在.NET 7+中判断路径是否指向同一文件/文件夹
针对你的需求,这里提供两种适配不同场景的方案,分别对应「纯文本比较(不访问文件系统)」和「适配文件系统规则(轻度访问)」的要求:
方案一:基于操作系统默认行为的纯文本比较
这个方案无需访问文件系统,仅通过规范化路径+系统默认的路径比较逻辑实现,能覆盖大部分通用场景:
- 先用
Path.GetFullPath将路径转换为绝对路径,自动处理相对路径、./..等冗余部分; - 调用
Path.Equals方法,该方法会根据操作系统的默认文件系统行为选择比较规则:- Windows:默认不区分大小写(如
C:\A.TXT和c:\a.txt会被判定为相同) - Linux/Unix:严格区分大小写
- macOS:遵循系统默认的APFS设置(默认不区分,若为区分大小写的APFS卷则不适用)
- Windows:默认不区分大小写(如
代码示例:
public static bool ArePathsSame(string path1, string path2) { string fullPath1 = Path.GetFullPath(path1); string fullPath2 = Path.GetFullPath(path2); return Path.Equals(fullPath1, fullPath2); }
局限:无法处理同一系统内不同文件系统的差异,比如Windows上设置为区分大小写的NTFS文件夹、macOS上的区分大小写APFS卷。
方案二:适配文件系统卷的大小写规则
如果需要精准适配具体文件系统的大小写规则(比如区分大小写的NTFS),需要轻度访问文件系统查询卷的元数据(不会打开或修改文件),再选择对应比较逻辑:
跨平台辅助方法:判断文件系统是否区分大小写
using System.ComponentModel; using System.Runtime.InteropServices; public static class FileSystemHelper { public static bool IsFileSystemCaseSensitive(string path) { string rootPath = Path.GetPathRoot(path) ?? throw new ArgumentException("无效路径", nameof(path)); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return IsWindowsFileSystemCaseSensitive(rootPath); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { return IsUnixFileSystemCaseSensitive(path); } throw new PlatformNotSupportedException("不支持的操作系统"); } private static bool IsWindowsFileSystemCaseSensitive(string rootPath) { const uint FILE_CASE_SENSITIVE_SEARCH = 0x00000001; uint flags = 0; StringBuilder fsName = new(256); if (!GetVolumeInformation(rootPath, null, 0, out _, out _, out flags, fsName, fsName.Capacity)) { throw new Win32Exception(); } return (flags & FILE_CASE_SENSITIVE_SEARCH) != 0; } private static bool IsUnixFileSystemCaseSensitive(string path) { Statfs buf = new(); if (statfs(path, ref buf) != 0) { throw new Win32Exception(); } const long ST_CASEINSENSITIVE = 0x00000002; return (buf.f_flags & ST_CASEINSENSITIVE) == 0; } [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool GetVolumeInformation( string lpRootPathName, StringBuilder? lpVolumeNameBuffer, int nVolumeNameSize, out uint lpVolumeSerialNumber, out uint lpMaximumComponentLength, out uint lpFileSystemFlags, StringBuilder lpFileSystemNameBuffer, int nFileSystemNameSize); [DllImport("libc", SetLastError = true)] private static extern int statfs(string path, ref Statfs buf); [StructLayout(LayoutKind.Sequential)] private struct Statfs { public long f_type; public long f_bsize; public long f_blocks; public long f_bfree; public long f_bavail; public long f_files; public long f_ffree; public ulong f_fsid; public long f_namelen; public long f_frsize; public long f_flags; public long[] f_spare; } }
路径比较方法
public static bool ArePathsSame(string path1, string path2) { string fullPath1 = Path.GetFullPath(path1); string fullPath2 = Path.GetFullPath(path2); // 先检查是否在同一根目录,避免跨文件系统的情况 if (!Path.GetPathRoot(fullPath1).Equals(Path.GetPathRoot(fullPath2), StringComparison.Ordinal)) { return false; } bool isCaseSensitive = FileSystemHelper.IsFileSystemCaseSensitive(fullPath1); StringComparison comparison = isCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; return fullPath1.Equals(fullPath2, comparison); }
路径规范化方法(Canonical)
public static string CanonicalizePath(string path) { string fullPath = Path.GetFullPath(path); bool isCaseSensitive = FileSystemHelper.IsFileSystemCaseSensitive(fullPath); return isCaseSensitive ? fullPath : fullPath.ToLowerInvariant(); }
说明:这个方案会查询路径所在卷的文件系统属性,能准确处理区分大小写的NTFS、APFS等场景,且不会打开或修改文件,仅读取卷的元数据。
注意事项
- 两种方案都不会处理符号链接、硬链接等情况,符合你“不关心这类路径是否指向同一实体”的要求;
- 方案一的优势是完全不访问文件系统,性能更高;方案二的优势是精准适配文件系统规则,适用范围更广。
内容的提问来源于stack exchange,提问作者ispiro
相关产品推荐
相关产品推荐

