如何比较C#中的(目录)路径?

Eam*_*nne 70 .net c# filesystems path

如果我有两个DirectoryInfo对象,我如何比较它们的语义相等?例如,以下路径应全部视为等于C:\temp:

  • C:\temp
  • C:\temp\
  • C:\temp\.
  • C:\temp\x\..\..\temp\.

以下可能等于或不等于C:\temp:

  • \temp 如果当前工作目录在驱动器上 C:\
  • temp 如果当前工作目录是 C:\
  • C:\temp.
  • C:\temp...\

如果考虑当前的工作目录很重要,我可以自己解决这个问题,所以这并不重要.尾随点在窗口中被剥离,因此这些路径确实应该相等 - 但它们不会在unix中被剥离,所以在mono下我会期望其他结果.

区分大小写是可选的.路径可能存在也可能不存在,用户可能拥有或可能没有路径权限 - 我更喜欢快速健壮的方法,不需要任何I/O(所以没有权限检查),但是如果有什么内置的话 - 我对任何"足够好"的东西都很满意......

Vla*_*adV 92

GetFullPath似乎做了工作,除了case difference(Path.GetFullPath("test") != Path.GetFullPath("TEST"))和尾部斜杠.因此,以下代码应该可以正常工作:

String.Compare(
    Path.GetFullPath(path1).TrimEnd('\\'),
    Path.GetFullPath(path2).TrimEnd('\\'), 
    StringComparison.InvariantCultureIgnoreCase)
Run Code Online (Sandbox Code Playgroud)

或者,如果你想开始DirectoryInfo:

String.Compare(
    dirinfo1.FullName.TrimEnd('\\'),
    dirinfo2.FullName.TrimEnd('\\'), 
    StringComparison.InvariantCultureIgnoreCase)
Run Code Online (Sandbox Code Playgroud)

  • 也许你应该使用System.IO.Path.DirectorySeparatorChar而不是'\\'. (8认同)
  • 您可以使用Path.GetFullPath(pathx).ToUpperInvariant().TrimEnd('\\')来消除区分大小写.但是在UNIX上应该谨慎使用,因为UNIX将不同大小写的两个名称视为不同的文件夹,而Windows将它们视为同一个. (2认同)
  • 哦,以防其他一些用户偶然发现这个答案; FullName*确实*需要路径发现安全权限,并且对当前工作目录敏感(这实际上意味着您只能比较绝对路径 - 或者在CWD中评估的相对路径). (2认同)

naw*_*fal 34

这个答案,这个方法可以处理一些边缘情况:

public static string NormalizePath(string path)
{
    return Path.GetFullPath(new Uri(path).LocalPath)
               .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
               .ToUpperInvariant();
}
Run Code Online (Sandbox Code Playgroud)

原始答案中的更多细节.称之为:

bool pathsEqual = NormalizePath(path1) == NormalizePath(path2);
Run Code Online (Sandbox Code Playgroud)

应该适用于文件和目录路径.

  • `new Uri(path).LocalPath` - 如果路径中有`#`符号,则会给出错误的路径 (4认同)
  • 在文件系统路径上使用了`ToUpperInvariant`吗?恭喜你 现在,您有了一个应用程序,有机会在带有土耳其语区域设置的操作系统上神秘地爆炸。 (3认同)
  • @Ishmaeel你是对的。对于其他所有人...请考虑此处的答案作为起点。主要思想是给出为什么应该处理大小写的要点(而不是在像 Windows 这样不区分大小写的操作系统上忽略)。 (2认同)

Ste*_*ven 11

在.NET中实现路径有一些简短的方法.有很多关于它的抱怨.NDepend的创建者Patrick Smacchia发布了一个开源库,可以处理常见和复杂的路径操作.如果在应用程序的路径上执行大量比较操作,则此库可能对您有用.

  • 我用它来确定一个目录是否包含另一个目录(例如C:\ A/B包含C:\ a\b\c\d\e\..\..\..\f\g)并且它非常有用好. (3认同)

Dav*_*osh 8

我意识到这是一个老帖子,但所有答案最终都是基于两个名字的文本比较.试图获得两个"规范化"名称,几乎不可能考虑到引用相同文件对象的无数可能方式.存在诸如:交汇点,符号链接,网络文件共享(以不同方式引用相同文件对象)等等的问题. 事实上,除了Igor Korkhov之外,上面的每个答案绝对会给出不正确的结果.某些情况(例如路口,符号链接,目录链接等)

这个问题特别要求解决方案不需要任何I/O,但是如果你要处理网络路径,你绝对需要做IO:有些情况下根本无法从任何本地路径字符串确定操作,两个文件引用是否会引用相同的物理文件.(这可以很容易理解如下.假设文件服务器在共享子树中的某个地方有一个Windows目录连接.在这种情况下,可以直接或通过连接引用文件.但是联结位于文件服务器上,因此,客户端根本无法通过本地信息确定两个引用文件名是指同一个物理文件:客户端本地无法获得信息.因此必须绝对做一些最小的IO - 例如,打开两个文件对象句柄 - 确定引用是否引用相同的物理文件.)

以下解决方案执行一些IO,尽管非常小,但正确地确定两个文件系统引用是否在语义上相同,即引用相同的文件对象.(如果两个文件规范都没有引用有效的文件对象,则所有投注均关闭):

    public static bool AreFileSystemObjectsEqual(string dirName1, string dirName2)
    {
        //Optimization: if strings are equal, don't bother with the IO
        bool bRet = string.Equals(dirName1, dirName2, StringComparison.OrdinalIgnoreCase);
        if (!bRet)
        {
            //NOTE: we cannot lift the call to GetFileHandle out of this routine, because we _must_
            // have both file handles open simultaneously in order for the objectFileInfo comparison
            // to be guaranteed as valid.
            using (SafeFileHandle directoryHandle1 = GetFileHandle(dirName1), directoryHandle2 = GetFileHandle(dirName2))
            {
                BY_HANDLE_FILE_INFORMATION? objectFileInfo1 = GetFileInfo(directoryHandle1);
                BY_HANDLE_FILE_INFORMATION? objectFileInfo2 = GetFileInfo(directoryHandle2);
                bRet = objectFileInfo1 != null
                       && objectFileInfo2 != null
                       && (objectFileInfo1.Value.FileIndexHigh == objectFileInfo2.Value.FileIndexHigh)
                       && (objectFileInfo1.Value.FileIndexLow == objectFileInfo2.Value.FileIndexLow)
                       && (objectFileInfo1.Value.VolumeSerialNumber == objectFileInfo2.Value.VolumeSerialNumber);
            }
        }
        return bRet;
    }
Run Code Online (Sandbox Code Playgroud)

这个想法来自Warren Stevens在我在SuperUser上发布的类似问题的回复:https://superuser.com/a/881966/241981


Igo*_*hov 5

似乎 P/Invoking GetFinalPathNameByHandle()将是最可靠的解决方案。

UPD:哎呀,我没有考虑到你不想使用任何 I/O

  • @Eamon Nerbonne:我的解决方案还有两个缺点:1) 它仅适用于 Vista 和更新的操作系统 2) 如果至少其中一个路径不存在,它将无法工作。但它也有一个好处:它适用于符号链接,即回答您的问题“我如何比较它们的语义相等性?”;而`GetFullPath()` 没有。因此,由您决定是否需要*真正的*语义平等。 (6认同)