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

Posted

技术标签:

【中文标题】如何比较 C# 中的(目录)路径?【英文标题】:How can I compare (directory) paths in C#? 【发布时间】:2011-01-17 21:36:56 【问题描述】:

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

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

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

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

如果考虑当前工作目录很重要,我可以自己弄清楚,所以这并不重要。尾随点在窗口中被剥离,所以这些路径真的应该是相等的 - 但它们在 unix 中没有被剥离,所以在单声道下我期待其他结果。

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

我意识到,如果没有 I/O,就无法确定某个中间存储层是否碰巧将相同的存储映射到了相同的文件(即使使用 I/O,当事情变得足够混乱时也可能是不可能的)。但是,无论底层文件系统如何,至少应该可以肯定地识别等效的路径,即在给定类型的所有可能文件系统上必然会解析为相同文件(如果存在)的路径。这有时有用的原因是 (A) 因为我当然想在执行 I/O 之前先检查一下,(B) I/O 有时会触发有问题的副作用,以及 (C) 各种其他软件组件有时会破坏提供的路径,并且能够以一种对等效路径的最常见转换不敏感的方式进行比较是有帮助的,最后 (D) 准备部署,事先进行一些健全性检查很有用,但这些检查发生在待部署之前- on system 甚至可以访问。

【问题讨论】:

相关问题:How to check whether 2 DirectoryInfo objects are pointing to the same directory? ***.com/questions/26230962/… 为什么System.IO.DirectoryInfo 类没有实现bool Equals(DirectoryInfo other) 来处理这个问题?在我看来,这些东西现在应该如此标准化,以至于我们甚至不应该能够搞砸简单的事情,比如比较两条路径。 这能回答你的问题吗? How to check whether 2 DirectoryInfo objects are pointing to the same directory? @GoodNightNerdPride 否,主要是因为该问题并未阐明路径相同意味着什么;例如需要处理不存在的路径。我正在寻找 path 等价性,而不是 特定系统上的文件系统对象等价性 【参考方案1】:

GetFullPath 似乎可以完成这项工作,除了大小写差异 (Path.GetFullPath("test") != Path.GetFullPath("TEST")) 和斜杠。 因此,以下代码应该可以正常工作:

String.Compare(
    Path.GetFullPath(path1).TrimEnd('\\'),
    Path.GetFullPath(path2).TrimEnd('\\'), 
    StringComparison.InvariantCultureIgnoreCase)

或者,如果你想以DirectoryInfo开头:

String.Compare(
    dirinfo1.FullName.TrimEnd('\\'),
    dirinfo2.FullName.TrimEnd('\\'), 
    StringComparison.InvariantCultureIgnoreCase)

【讨论】:

你可以做 Path.GetFullPath(pathx).ToUpperInvariant().TrimEnd('\\') 摆脱区分大小写。但是在 UNIX 上应该谨慎使用,因为 UNIX 将不同大小写的两个名称视为不同的文件夹,而 Windows 将它们视为一个并且相同。 如果您将其编辑为不区分大小写并使用 DirectoryInfo(通过 FullName),您将得到一个完美的答案 :-) @Eamon,我已经为你添加了 DirectoryInfo 变体:-)。它已经是大小写不变的——这就是 StringComparison.InvariantCultureIgnoreCase 所做的。 哦,万一其他用户偶然发现了这个答案; FullName 确实需要路径发现安全权限并且对当前工作目录敏感(这实际上意味着您只能比较绝对路径 - 或在 CWD 中评估的相对路径)。 也许你应该使用 System.IO.Path.DirectorySeparatorChar 而不是 '\\'。【参考方案2】:

来自this answer,这个方法可以处理一些边缘情况:

public static string NormalizePath(string path)

    return Path.GetFullPath(new Uri(path).LocalPath)
               .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
               .ToUpperInvariant();

原始答案中的更多详细信息。像这样称呼它:

bool pathsEqual = NormalizePath(path1) == NormalizePath(path2);

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

【讨论】:

有点有趣的是,这实际上是我最终做的,因为它没有 I/O! new Uri(path).LocalPath - 如果路径中有# 符号,则会给出错误的路径 在某些情况下,特别是对于网络文件,这会给出错误的答案。事实上,在处理网络文件时,在某些情况下,如果不进行任何 IO,即不处理文件句柄,根本无法正确确定答案。有关“更正确”的解决方案,请参阅下面的答案,该解决方案诚然使用 IO 与问题中的具体要求相反。 在文件系统路径上使用了ToUpperInvariant?恭喜。您现在有一个应用程序有可能在具有土耳其区域设置的操作系统上神秘地崩溃。 @Ishmaeel 你是对的。对于其他所有人......请考虑这里的答案作为起点。主要思想是给出一个关于为什么要处理大小写的要点(在 Windows 等不区分大小写的操作系统上忽略)。【参考方案3】:

自最初提出问题以来以及最初发布此答案以来,已对该问题进行了编辑和澄清。就目前的问题而言,下面的这个答案不是正确的答案。本质上,当前的问题是要求进行纯文本路径比较,这与想要确定两条路径是否解析为同一个文件系统对象完全不同。所有其他答案,除了 Igor Korkhov 的答案,最终都是基于两个名字的文本比较。

如果你真的想知道两个路径何时解析到同一个文件系统对象,你必须做一些 IO。试图获得两个“标准化”名称,考虑到引用同一文件对象的无数可能方式,几乎是不可能的。存在诸如:连接点、符号链接、网络文件共享(以不同方式引用同一文件对象)等问题。事实上,除了 Igor Korkhov 的之外,上面的每个答案都绝对会给出在某些情况下不正确导致问题“这两个路径是否引用相同的文件系统对象。(例如连接、符号链接、目录链接等)

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

以下解决方案会进行一些 IO,虽然非常少,但可以正确确定两个文件系统引用在语义上是否相同,即引用同一个文件对象。 (如果两个文件规范都没有引用有效的文件对象,则所有赌注都关闭):

public static bool AreDirsEqual(string dirName1, string dirName2, bool resolveJunctionaAndNetworkPaths = true)

    if (string.IsNullOrEmpty(dirName1) || string.IsNullOrEmpty(dirName2))
        return dirName1==dirName2;
    dirName1 = NormalizePath(dirName1); //assume NormalizePath normalizes/fixes case and path separators to Path.DirectorySeparatorChar
    dirName2 = NormalizePath(dirName2);
    int i1 = dirName1.Length;
    int i2 = dirName2.Length;
    do
    
        --i1; --i2;
        if (i1 < 0 || i2 < 0)
            return i1 < 0 && i2 < 0;
     while (dirName1[i1] == dirName2[i2]);//If you want to deal with international character sets, i.e. if NormalixePath does not fix case, this comparison must be tweaked
    if( !resolveJunctionaAndNetworkPaths )
        return false;
    for(++i1, ++i2; i1 < dirName1.Length; ++i1, ++i2)
    
        if (dirName1[i1] == Path.DirectorySeparatorChar)
        
            dirName1 = dirName1.Substring(0, i1);
            dirName2 = dirName1.Substring(0, i2);
            break;
        
    
    return AreFileSystemObjectsEqual(dirName1, dirName2);


public static bool AreFileSystemObjectsEqual(string dirName1, string dirName2)

    //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);
        return objectFileInfo1 != null
                && objectFileInfo2 != null
                && (objectFileInfo1.Value.FileIndexHigh == objectFileInfo2.Value.FileIndexHigh)
                && (objectFileInfo1.Value.FileIndexLow == objectFileInfo2.Value.FileIndexLow)
                && (objectFileInfo1.Value.VolumeSerialNumber == objectFileInfo2.Value.VolumeSerialNumber);
    


static SafeFileHandle GetFileHandle(string dirName)

    const int FILE_ACCESS_NEITHER = 0;
    //const int FILE_SHARE_READ = 1;
    //const int FILE_SHARE_WRITE = 2;
    //const int FILE_SHARE_DELETE = 4;
    const int FILE_SHARE_ANY = 7;//FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE
    const int CREATION_DISPOSITION_OPEN_EXISTING = 3;
    const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
    return CreateFile(dirName, FILE_ACCESS_NEITHER, FILE_SHARE_ANY, System.IntPtr.Zero, CREATION_DISPOSITION_OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, System.IntPtr.Zero);



static BY_HANDLE_FILE_INFORMATION? GetFileInfo(SafeFileHandle directoryHandle)

    BY_HANDLE_FILE_INFORMATION objectFileInfo;
    if ((directoryHandle == null) || (!GetFileInformationByHandle(directoryHandle.DangerousGetHandle(), out objectFileInfo)))
    
        return null;
    
    return objectFileInfo;


[DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode, SetLastError = true)]
static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode,
 IntPtr SecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool GetFileInformationByHandle(IntPtr hFile, out BY_HANDLE_FILE_INFORMATION lpFileInformation);

[StructLayout(LayoutKind.Sequential)]
public struct BY_HANDLE_FILE_INFORMATION

    public uint FileAttributes;
    public System.Runtime.InteropServices.ComTypes.FILETIME CreationTime;
    public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime;
    public System.Runtime.InteropServices.ComTypes.FILETIME LastWriteTime;
    public uint VolumeSerialNumber;
    public uint FileSizeHigh;
    public uint FileSizeLow;
    public uint NumberOfLinks;
    public uint FileIndexHigh;
    public uint FileIndexLow;
;

请注意,在上面的代码中,我包含了两行类似dirName1 = NormalizePath(dirName1); 的代码,并且没有指定函数NormalizePath 是什么。 NormalizePath 可以是任何路径规范化函数 - 许多已在此问题其他地方的答案中提供。提供合理的NormalizePath 函数意味着AreDirsEqual 将给出合理的答案,即使两个输入路径引用不存在的文件系统对象,即您只想在字符串级别上比较的路径。 (上面 Ishmaeel 的评论也应该注意,这段代码没有这样做......)

(此代码可能存在细微的权限问题,如果用户对某些初始目录具有遍历权限,我不确定是否允许AreFileSystemObjectsEqual 要求的文件系统访问。在这种情况下,参数resolveJunctionaAndNetworkPaths 至少允许用户恢复到纯文本比较...)

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

【讨论】:

此答案不适用于尚不存在的路径,但对于您有权查看本地 smb 句柄的现有对象来说,它非常漂亮。通常,任何路径都可以重新映射以引用相同的存储;因此,两个现有的可访问文件系统对象是否引用同一个存储的问题是不同的(并且可能是一个没有完全正确答案的对象,因为它取决于其间的各种层)。 @Eamon.Nerbonne - 感谢您的编辑。回复:“此答案不适用于尚不存在的路径” - 正确,因此我在上面的答案中的评论:“如果两个文件规范都没有引用有效的文件对象,则所有赌注都关闭”。请注意,如果一个引用有效的文件对象而另一个没有引用,则它们显然不相等,并且在这种情况下,上面的代码将正确返回“false”。如果两者都不存在但字符串相等,则返回true,这也是正确的。如果两者都不存在并且它们在字符串级别不相等,则返回 false,请参阅答案中新添加的 cmets。 从技术上讲,假设两个字符串路径 dirNameX 引用不存在的文件系统对象,并假设 NormalizePath 将所有路径分隔符规范化为 \ 。假设两个字符串路径的形式为:\, \,其中 指的是同一个存在的目录(但如前所述,可能不相等在字符串级别)。那么实际上可以合理地说这两个 dirNameX 指的是同一路径。上面新提供的代码说明了这一点。代码至今未经测试...【参考方案4】:

.NET 中路径的实现有一些不足之处。对此有很多抱怨。 NDepend 的创建者Patrick Smacchia 发布了open source library that enables handling of common and complex path operations。如果您在应用程序中对路径进行大量比较操作,此库可能对您有用。

【讨论】:

嗯,有意思——你用过吗? 我已经用它来确定一个目录是否包含另一个目录(例如 C:\A/B 包含 C:\a\b\c\d\e\..\..\..\ f\g) 效果很好。【参考方案5】:

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

UPD:糟糕,我没有考虑到您不使用任何 I/O 的愿望

【讨论】:

嗯,我宁愿没有 I/O,但是使用简单 I/O 的解决方案比从头开始编写要好... @Eamon Nerbonne:我的解决方案还有两个缺点:1) 它仅适用于 Vista 和更新的操作系统 2) 如果至少有一个路径不存在,它将无法工作。但它也有一个好处:它适用于符号链接,即回答您的问题“我如何比较它们的语义相等性?”;而GetFullPath() 没有。所以由你决定是否需要真正的语义平等。【参考方案6】:

微软已经实现了类似的方法,尽管它们不如上面的答案有用:

PathUtil.ArePathsEqual Method(就是return string.Equals(path1, path2, StringComparison.OrdinalIgnoreCase);) PathUtil.Normalize Method PathUtil.NormalizePath Method(就是return PathUtil.Normalize(path);

【讨论】:

【参考方案7】:
 System.IO.Path.GetFullPath(pathA).Equals(System.IO.Path.GetFullPath(PathB));

【讨论】:

System.IO.Path.GetFullPath(@"C:\LOL").Equals(System.IO.Path.GetFullPath(@"C:\LOL\")) 返回错误【参考方案8】:

“名称”属性是相等的。采取:

DirectoryInfo dir1 = new DirectoryInfo("C:\\Scratch");
DirectoryInfo dir2 = new DirectoryInfo("C:\\Scratch\\");
DirectoryInfo dir3 = new DirectoryInfo("C:\\Scratch\\4760");
DirectoryInfo dir4 = new DirectoryInfo("C:\\Scratch\\4760\\..\\");

dir1.Name == dir2.Name and dir2.Name == dir4.Name(在本例中为“Scratch”。dir3 ==“4760”。)只有 FullName 属性不同。

在给定两个 DirectoryInfo 类的情况下,您可以使用递归方法检查每个父级的 Name 属性,以确保完整路径相同。

编辑:这对您的情况有用吗?创建一个控制台应用程序并将其粘贴到整个 Program.cs 文件中。向 AreEquals() 函数提供两个 DirectoryInfo 对象,如果它们是同一个目录,它将返回 True。如果您愿意,您也许可以将此 AreEquals() 方法调整为 DirectoryInfo 上的扩展方法,因此您可以这样做 myDirectoryInfo.IsEquals(myOtherDirectoryInfo);

using System;
using System.Diagnostics;
using System.IO;
using System.Collections.Generic;

namespace ConsoleApplication3

    class Program
    
        static void Main(string[] args)
        
            Console.WriteLine(AreEqual(
                new DirectoryInfo("C:\\Scratch"),
                new DirectoryInfo("C:\\Scratch\\")));

            Console.WriteLine(AreEqual(
                new DirectoryInfo("C:\\Windows\\Microsoft.NET\\Framework"),
                new DirectoryInfo("C:\\Windows\\Microsoft.NET\\Framework\\v3.5\\1033\\..\\..")));

            Console.WriteLine(AreEqual(
                new DirectoryInfo("C:\\Scratch\\"),
                new DirectoryInfo("C:\\Scratch\\4760\\..\\..")));

            Console.WriteLine("Press ENTER to continue");
            Console.ReadLine();
        

        private static bool AreEqual(DirectoryInfo dir1, DirectoryInfo dir2)
        
            DirectoryInfo parent1 = dir1;
            DirectoryInfo parent2 = dir2;

            /* Build a list of parents */
            List<string> folder1Parents = new List<string>();
            List<string> folder2Parents = new List<string>();

            while (parent1 != null)
            
                folder1Parents.Add(parent1.Name);
                parent1 = parent1.Parent;
            

            while (parent2 != null)
            
                folder2Parents.Add(parent2.Name);
                parent2 = parent2.Parent;
            

            /* Now compare the lists */

            if (folder1Parents.Count != folder2Parents.Count)
            
                // Cannot be the same - different number of parents
                return false;
            

            bool equal = true;

            for (int i = 0; i < folder1Parents.Count && i < folder2Parents.Count; i++)
            
                equal &= folder1Parents[i] == folder2Parents[i];
            

            return equal;
        
    

【讨论】:

Name 属性只会返回最深子目录的名称,因此“c:\foo\bar”将返回“bar”。将 "d:\foo\bar" 与 "c:\bar" 进行比较会得到 true,这是不好的。 这就是为什么要对所有父母进行递归比较!请删除反对票,这是一个完全可以接受的解决方案。我用完整的代码示例修改了我的答案。 嗯,这甚至​​适用于 D:\tempC:\temp。好主意;你没有处理区分大小写,它可能会更短一些:while(dir1!=null && dir2!=null) if(!string.Equals(dir1.Name,dir2.Name,StringComparison.InvariantCultureIgnoreCase))返回假;否则 dir1=dir1.Parent; dir2=dir2.Parent; 返回 dir1==dir2; 是的,区分大小写是一个困难的问题,因为 OP 希望代码在 Mono 和 Windows 上都可以工作,但在 Linux 2 上,不同大小写的名称被认为是不同的,但在 Windows 上,它们被认为是相同的文件,因此这是每个平台的决定。【参考方案9】:

您可以使用 Minimatch,它是 Node.js 的 minimatch 的一个端口。

var mm = new Minimatcher(searchPattern, new Options  AllowWindowsPaths = true );

if (mm.IsMatch(somePath))

    // The path matches!  Do some cool stuff!


var matchingPaths = mm.Filter(allPaths);

See why AllowWindowsPaths = true 选项是必需的:

在 Windows 风格的路径上 Minimatch 的语法是为 Linux 风格的路径设计的(只有正斜杠)。特别是,它使用反斜杠作为转义字符,因此它不能简单地接受 Windows 样式的路径。我的 C# 版本保留了这种行为。

要抑制这种情况,并允许反斜杠和正斜杠作为路径分隔符(在模式或输入中),请设置 AllowWindowsPaths 选项:

var mm = new Minimatcher(searchPattern, new Options  AllowWindowsPaths = true );

传递此选项将完全禁用转义字符。

Nuget: http://www.nuget.org/packages/Minimatch/

GitHub: https://github.com/SLaks/Minimatch

【讨论】:

【参考方案10】:
using System;
using System.Collections.Generic;
using System.Text;

namespace EventAnalysis.IComparerImplementation


    public sealed class FSChangeElemComparerByPath : IComparer<FSChangeElem>
    
        public int Compare(FSChangeElem firstPath, FSChangeElem secondPath)
        
            return firstPath.strObjectPath == null ?
                (secondPath.strObjectPath == null ? 0 : -1) :
                (secondPath.strObjectPath == null ? 1 : ComparerWrap(firstPath.strObjectPath, secondPath.strObjectPath));
        

        private int ComparerWrap(string stringA, string stringB)
        
            int length = 0;
            int start = 0;
            List<string> valueA = new List<string>();
            List<string> valueB = new List<string>();

            ListInit(ref valueA, stringA);
            ListInit(ref valueB, stringB);

            if (valueA.Count != valueB.Count)
            
                length = (valueA.Count > valueB.Count)
                           ? valueA.Count : valueB.Count;

                if (valueA.Count != length)
                
                    for (int i = 0; i < length - valueA.Count; i++)
                    
                        valueA.Add(string.Empty);
                    
                
                else
                
                    for (int i = 0; i < length - valueB.Count; i++)
                    
                        valueB.Add(string.Empty);
                    
                
            

            else
                length = valueA.Count;

            return RecursiveComparing(valueA, valueB, length, start);
        

        private void ListInit(ref List<string> stringCollection, string stringToList)
        
            foreach (string s in stringToList.Remove(0, 2).Split('\\'))
            
                stringCollection.Add(s);
            
        

        private int RecursiveComparing(List<string> valueA, List<string> valueB, int length, int start)
        
            int result = 0;

            if (start != length)
            
                if (valueA[start] == valueB[start])
                
                    result = RecursiveComparing(valueA, valueB, length, ++start);
                
                else
                
                    result = String.Compare(valueA[start], valueB[start]);
                
            
            else
                return 0;

            return result;
        
    

【讨论】:

列表 _list = 新列表(bla-bla-bla); _list.Sort(new FSChangeElemComparerByPath());【参考方案11】:

我自己用递归解决了这个问题。

 public bool PathEquals(string Path1, string Path2)
 
     FileInfo f1 = new FileInfo(Path1.Trim('\\','/','.'));
     FileInfo f2 = new FileInfo(Path2.Trim('\\', '/','.'));
     if(f1.Name.ToLower() == f2.Name.ToLower())
     
         return DirectoryEquals(f1.Directory, f2.Directory);
     
     else
     
         return false;
     


public bool DirectoryEquals(DirectoryInfo d1, DirectoryInfo d2)

    if(d1.Name.ToLower() == d2.Name.ToLower())
    
        if((d1.Parent != null) && (d2.Parent != null))
        
            return DirectoryEquals(d1.Parent, d2.Parent);
        
        else
        
            return true;//C:\Temp1\Temp2 equals \Temp1\Temp2
            //return (d1.Parent == null) && (d2.Parent == null);//C:\Temp1\Temp2 does not equal \Temp1\Temp2
        
    
    else
    
        return false;
    

注意:new FileInfo(path) 会返回有效的 FileInfo,即使路径不是文件(名称字段等于目录名称)

【讨论】:

【参考方案12】:

谢谢@Andy Shellam 和 @VladV 和 @Eamon Nerbonne

我找到了另一个解决方案:

        private static bool AreEqual(DirectoryInfo dir1, DirectoryInfo dir2)
        
            return AreEqual(dir1.FullName, dir2.FullName);
        

        private static bool AreEqual(string folderPath1, string folderPath2)
        
            folderPath1 = Path.GetFullPath(folderPath1);
            folderPath2 = Path.GetFullPath(folderPath2);

            if (folderPath1.Length == folderPath2.Length)
            
                return string.Equals(folderPath1, folderPath2/*, StringComparison.OrdinalIgnoreCase*/);
            
            else if (folderPath1.Length == folderPath2.Length + 1 && IsEndWithAltDirectorySeparatorChar(folderPath1))
            
                // folderPath1 = @"F:\temp\"
                // folderPath2 = @"F:\temp"
                return folderPath1.Contains(folderPath2 /*, StringComparison.OrdinalIgnoreCase*/);
            
            else if (folderPath1.Length + 1 == folderPath2.Length && IsEndWithAltDirectorySeparatorChar(folderPath2))
            
                // folderPath1 = @"F:\temp"
                // folderPath2 = @"F:\temp\"
                return folderPath2.Contains(folderPath1 /*, StringComparison.OrdinalIgnoreCase*/);
            

            return false;

            static bool IsEndWithAltDirectorySeparatorChar(string path)
            
                var lastChar = path[path.Length - 1];
                return lastChar == Path.DirectorySeparatorChar;
            
        

它可以很好地工作。

        static void Main(string[] args)
        
            Console.WriteLine(AreEqual(
                new DirectoryInfo("C:\\Scratch"),
                new DirectoryInfo("C:\\Scratch\\")));

            Console.WriteLine(AreEqual(
                new DirectoryInfo("C:\\Windows\\Microsoft.NET\\Framework"),
                new DirectoryInfo("C:\\Windows\\Microsoft.NET\\Framework\\v3.5\\1033\\..\\..")));

            Console.WriteLine(AreEqual(
                new DirectoryInfo("C:\\Scratch\\"),
                new DirectoryInfo("C:\\Scratch\\4760\\..\\..")));

            Debug.WriteLine(AreEqual(@"C:/Temp", @"C:\Temp2")); // False
            Debug.WriteLine(AreEqual(@"C:\Temp\", @"C:\Temp2"));// False
            Debug.WriteLine(AreEqual(@"C:\Temp\", @"C:\Temp")); // True
            Debug.WriteLine(AreEqual(@"C:\Temp/", @"C:\Temp")); // True
            Debug.WriteLine(AreEqual(@"C:/Temp/", @"C:\Temp\"));// True

            Console.WriteLine("Press ENTER to continue");
            Console.ReadLine();
        

【讨论】:

AreEqual(@"C:\Temp", @"C:\Temp2") 呢?【参考方案13】:
bool equals = myDirectoryInfo1.FullName == myDirectoryInfo2.FullName;

?

【讨论】:

这是 Binary Warrier 解决方案的简单版本。请注意,尾部斜杠存在问题:“c:\temp”不等于“c:\temp\”。 好的,所以如果我规范化尾部斜杠和可能的大小写 - 并接受它执行一些 FileIOPermission 的事实 - 这看起来是一个好的开始,谢谢!【参考方案14】:
bool Equals(string path1, string path2)

    return new Uri(path1) == new Uri(path2);

Uri 构造函数规范化路径。

【讨论】:

这基本上等同于@nawfal 的建议(这是目前公认的答案) 斜线的问题呢?

以上是关于如何比较 C# 中的(目录)路径?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 C# 中的单个完整路径创建多个目录?

如何从 C# 控制台应用程序中的目录获取路径?

C# 获取路径中的子目录

如何比较 tar 存档和目录中的文件列表?

C#如何遍历指定目录以及指定目录下所有子文件夹,并获取除指定文件类型以外的所有文件的绝对路径?

c# 文件操作