83

If I have two DirectoryInfo objects, how can I compare them for semantic equality? For example, the following paths should all be considered equal to C:\temp:

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

The following may or may not be equal to C:\temp:

  • \temp if the current working directory is on drive C:\
  • temp if the current working directory is C:\
  • C:\temp.
  • C:\temp...\

If it's important to consider the current working directory, I can figure that out myself, so that's not that important. Trailing dots are stripped in windows, so those paths really should be equal - but they aren't stripped in unix, so under mono I'd expect other results.

Case sensitivity is optional. The paths may or may not exist, and the user may or may not have permissions to the path - I'd prefer a fast robust method that doesn't require any I/O (so no permission checking), but if there's something built-in I'd be happy with anything "good enough" too...

I realize that without I/O it's not possible to determine whether some intermediate storage layer happens to have mapped the same storage to the same file (and even with I/O, when things get messy enough it's likely impossible). However, it should be possible to at least positively identify paths that are equivalent, regardless of the underlying filesystem, i.e. paths that necessarily would resolve to the same file (if it exists) on all possible file-systems of a given type. The reason this is sometimes useful is (A) because I certainly want to check this first, before doing I/O, (B) I/O sometimes triggers problematic side-effects, and (C) various other software components sometimes mangle paths provided, and it's helpful to be able to compare in a way that's insensitive to most common transformations of equivalent paths, and finally (D) to prepare deployments it's useful to do some sanity checks beforehand, but those occur before the to-be-deployed-on system is even accessible.

Eamon Nerbonne
  • 47,023
  • 20
  • 101
  • 166
  • 1
    Related question: [How to check whether 2 DirectoryInfo objects are pointing to the same directory?](http://stackoverflow.com/q/1794025/588306) – Deanna Oct 10 '12 at 13:46
  • http://stackoverflow.com/questions/26230962/c-sharp-file-path-string-comparison-case-insensitivity/39102087#39102087 – Mr.B Aug 23 '16 at 13:09
  • 1
    Why does the `System.IO.DirectoryInfo` class not implement `bool Equals(DirectoryInfo other)` to handle this? Seems to me that this stuff should be *so* standardized by now that we shouldn't even be *able* to mess up simple things like comparing two paths. – Elaskanator Jul 09 '19 at 21:22
  • Does this answer your question? [How to check whether 2 DirectoryInfo objects are pointing to the same directory?](https://stackoverflow.com/questions/1794025/how-to-check-whether-2-directoryinfo-objects-are-pointing-to-the-same-directory) – Good Night Nerd Pride Mar 30 '21 at 22:11
  • @GoodNightNerdPride no, largely because the question doesn't clarify what it means for paths to be identical; e.g. the need to deal with non-existing paths. I'm looking for *path* equivalence, not *file system object equivalence on a specific system*. – Eamon Nerbonne Apr 03 '21 at 12:01
  • Have you seen [this answer](https://stackoverflow.com/a/66877177/1025555) though? It detects almost all your examples cases as equal paths (except `temp` and `C:\temp...\ `) without any IO. – Good Night Nerd Pride Apr 03 '21 at 12:15

14 Answers14

101

GetFullPath seems to do the work, except for case difference (Path.GetFullPath("test") != Path.GetFullPath("TEST")) and trailing slash. So, the following code should work fine:

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

Or, if you want to start with DirectoryInfo:

String.Compare(
    dirinfo1.FullName.TrimEnd('\\'),
    dirinfo2.FullName.TrimEnd('\\'), 
    StringComparison.InvariantCultureIgnoreCase)
Matthew
  • 28,056
  • 26
  • 104
  • 170
VladV
  • 10,093
  • 3
  • 32
  • 48
  • 3
    You could do Path.GetFullPath(pathx).ToUpperInvariant().TrimEnd('\\') to get rid of the case sensitivity. This should be used with caution on UNIX though, as UNIX treats two names of different case as different folders, whereas Windows treats them as one and the same. – Andy Shellam Feb 17 '10 at 15:30
  • 1
    If you edit this to be case invariant and use DirectoryInfo's (via FullName), you'll have a perfect answer :-) – Eamon Nerbonne Feb 18 '10 at 12:53
  • @Eamon, I've added DirectoryInfo variant for you :-) . And it was case invariant already - that's what StringComparison.InvariantCultureIgnoreCase does. – VladV Feb 19 '10 at 09:19
  • 3
    Oh, and in case some other user stumbles across this answer; FullName *does* require path-discovery security permissions and is sensitive to the current-working-directory (which effectively means you can only compare absolute paths - or relative paths as evaluated in the CWD). – Eamon Nerbonne Feb 19 '10 at 10:26
  • Will this handle two paths to the same directory when one path uses a junction point? – Mashmagar Apr 18 '12 at 19:36
  • @Mashmagar, no, it won't. Only pathes are compared, not any real directories (as OP said, they might not exist at all). – VladV Apr 20 '12 at 12:42
  • 12
    Maybe you should use System.IO.Path.DirectorySeparatorChar instead of '\\'. – Pato Jul 04 '13 at 20:47
  • This will give incorrect answer in certain network file cases. If one is going to include networked files, it is NOT POSSIBLE to do this correctly without doing some IO - any answer that attempts to do this based on resolving path names WILL give incorrect answers in certain cases. As a result, almost every answer on this page WILL give incorrect answers for networked files in certain cases. – David I. McIntosh Apr 09 '20 at 04:47
45

From this answer, this method can handle a few edge cases:

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

More details in the original answer. Call it like:

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

Should work for both file and directory paths.

Community
  • 1
  • 1
nawfal
  • 70,104
  • 56
  • 326
  • 368
  • 1
    Somewhat amusingly, that's actually what I ended up doing since this has no I/O! – Eamon Nerbonne Jan 13 '14 at 10:35
  • 5
    `new Uri(path).LocalPath` - would give wrong path in case of `#` symbol in path – ili Aug 21 '14 at 16:52
  • 1
    There are cases, in particular for network files, where this will give the incorrect answer. In fact, when dealing with network files, there are cases where it will simply not be possible to correctly determine the answer without doing any IO, i.e. without dealing with file handles. See my answer below for a "more correct" solution, which admittedly uses IO contrary to the specific request in the question. – David I. McIntosh Sep 14 '16 at 19:13
  • 1
    Note that this will throw an exception for examples like "\temp" and "temp" in the question. – John Reynolds Feb 26 '17 at 17:04
  • If you stick in `Path.GetFullPath` before you pass the path to the `Uri` constructor then paths such as `.`, `..`, `temp`, etc. can be handled. Code will become `Path.GetFullPath(new Uri(Path.GetFullPath(path)).LocalPath).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar).ToUpperInvariant()` – user3613932 Jul 26 '17 at 02:25
  • 4
    Used `ToUpperInvariant` on a file system path? Congratulations. You now have an application that has a chance to blow up mysteriously on operating systems with Turkish regional settings. – Ishmaeel Nov 27 '17 at 10:45
  • 2
    @Ishmaeel you are right. To everyone others... Please consider answers here as starting point. The main idea was to give a gist on why case should be handled (rather ignored on case insensitive OS like Windows). – nawfal Apr 12 '21 at 06:17
12

The question has been edited and clarified since it was originally asked and since this answer was originally posted. As the question currently stands, this answer below is not a correct answer. Essentially, the current question is asking for a purely textual path comparison, which is quite different from wanting to determine if two paths resolve to the same file system object. All the other answers, with the exception of Igor Korkhov's, are ultimately based on a textual comparison of two names.

If one actually wants to know when two paths resolve to the same file system object, you must do some IO. Trying to get two "normalized" names, that take in to account the myriad of possible ways of referencing the same file object, is next to impossible. There are issues such as: junctions, symbolic links, network file shares (referencing the same file object in different manners), etc. etc. In fact, every single answer above, with the exception of Igor Korkhov's, will absolutely give incorrect results in certain circumstances to the question "do these two paths reference the same file system object. (e.g. junctions, symbolic links, directory links, etc.)

The question specifically requested that the solution not require any I/O, but if you are going to deal with networked paths, you will absolutely need to do IO: there are cases where it is simply not possible to determine from any local path-string manipulation, whether two file references will reference the same physical file. (This can be easily understood as follows. Suppose a file server has a windows directory junction somewhere within a shared subtree. In this case, a file can be referenced either directly, or through the junction. But the junction resides on, and is resolved by, the file server, and so it is simply impossible for a client to determine, purely through local information, that the two referencing file names refer to the same physical file: the information is simply not available locally to the client. Thus one must absolutely do some minimal IO - e.g. open two file object handles - to determine if the references refer to the same physical file.)

The following solution does some IO, though very minimal, but correctly determines whether two file system references are semantically identical, i.e. reference the same file object. (if neither file specification refers to a valid file object, all bets are off):

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;
};

Note that in the above code I have included two lines like dirName1 = NormalizePath(dirName1); and have not specified what the function NormalizePath is. NormalizePath can be any path-normalization function - many have been provided in answers elsewhere in this question. Providing a reasonable NormalizePath function means that AreDirsEqual will give a reasonable answer even when the two input paths refer to non-existent file system objects, i.e. to paths that you simply want to compare on a string-level. ( Ishmaeel's comment above should be paid heed as well, and this code does not do that...)

(There may be subtle permissions issues with this code, if a user has only traversal permissions on some initial directories, I am not sure if the file system accesses required by AreFileSystemObjectsEqual are permitted. The parameter resolveJunctionaAndNetworkPaths at least allows the user to revert to pure textual comparison in this case...)

The idea for this came from a reply by Warren Stevens in a similar question I posted on SuperUser: https://superuser.com/a/881966/241981

David I. McIntosh
  • 2,038
  • 4
  • 23
  • 45
  • 1
    This answer won't work for not-yet existing paths, but it's pretty nifty for existing objects that you have permissions for in the way it can look through local smb handles. In general, any path may be remapped to refer to the same storage; so the question of whether two existing accessible file system objects refer to the same storage is a different one (and likely one without a completely correct answer, because it depends on all kinds of layers in between). – Eamon Nerbonne Apr 03 '21 at 11:50
  • @Eamon.Nerbonne - Thanks for the edits. Re: "This answer won't work for not-yet existing paths" - correct, hence my comment in my answer above: "if neither file specification refers to a valid file object, all bets are off". Note that if one refers to a valid file object and the other does not, they are demonstrably not equal and the code above will correctly return "false" in this case. If neither exists but the strings are equal, it returns true, which is also correct. If neither exists and they are not equal at the string level, it returns false, see newly added comments in answer. – David I. McIntosh Apr 11 '21 at 17:44
  • Technically, suppose the two string paths dirNameX refer to non-existent file system objects, and suppose NormalizePath normalizes all path separators to \ . Suppose also the two string paths are of the form: \, \, where and refer to the same existent directory (but as mentioned, may not be equal at the string level). Then in fact one could reasonable say the two dirNameX's refer to the same path. Newly provided code above accounts for this. Code is so far untested... – David I. McIntosh Apr 11 '21 at 18:42
11

There are some short comes to the implementation of paths in .NET. There are many complaints about it. Patrick Smacchia, the creator of NDepend, published an open source library that enables handling of common and complex path operations. If you do a lot of compare operations on paths in your application, this library might be useful to you.

Community
  • 1
  • 1
Steven
  • 166,672
  • 24
  • 332
  • 435
5

It seems that P/Invoking GetFinalPathNameByHandle() would be the most reliable solution.

UPD: Oops, I didn't take into account your desire not to use any I/O

Igor Korkhov
  • 8,283
  • 1
  • 26
  • 31
  • Well, I'd *prefer* no I/O, but a simple I/O using solution is better than writing something from scratch... – Eamon Nerbonne Feb 17 '10 at 15:29
  • 7
    @Eamon Nerbonne: my solution has two more downsides: 1) it will work only on Vista and newer OSs 2) it won't work if at least one of the paths does not exist. But it also has one benefit: it works with symbolic links, i.e. answers your question "how can I compare them for semantic equality?"; while `GetFullPath()` doesn't. So it's up to you to decide if you need *real* semantic euqality or not. – Igor Korkhov Feb 17 '10 at 15:44
2

Microsoft has implemented similar methods, although they are not as useful as the answers above:

Chris Xue
  • 2,317
  • 1
  • 25
  • 21
1
 System.IO.Path.GetFullPath(pathA).Equals(System.IO.Path.GetFullPath(PathB));
Asad
  • 21,468
  • 17
  • 69
  • 94
  • 3
    System.IO.Path.GetFullPath(@"C:\LOL").Equals(System.IO.Path.GetFullPath(@"C:\LOL\")) returns false – 2xMax Dec 03 '15 at 12:21
1

The "Name" properties are equal. Take:

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" in this case. dir3 == "4760".) It's only the FullName properties that are different.

You might be able to do a recursive method to examine the Name properties of each parent given your two DirectoryInfo classes to ensure the complete path is the same.

EDIT: does this work for your situation? Create a Console Application and paste this over the entire Program.cs file. Provide two DirectoryInfo objects to the AreEquals() function and it will return True if they're the same directory. You might be able to tweak this AreEquals() method to be an extension method on DirectoryInfo if you like, so you could just do 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;
        }
    }
}
Andy Shellam
  • 15,403
  • 1
  • 27
  • 41
  • 1
    The Name property will only return the name of the deepest subdirectory, so "c:\foo\bar" will return "bar". Comparing "d:\foo\bar" with "c:\bar" will result true, which is not good. – Steven Feb 17 '10 at 15:11
  • That's why you do a *recursive* comparison on all parents! Please remove the downvote, this is a perfectly acceptable solution. I've modified my answer with a full code sample. – Andy Shellam Feb 17 '10 at 15:22
  • Hmm, this works even for `D:\temp` vs. `C:\temp`. Fine idea; you didn't deal with case sensitivity though, and it could be a bit shorter: while(dir1!=null && dir2!=null) if(!string.Equals(dir1.Name,dir2.Name,StringComparison.InvariantCultureIgnoreCase)) return false; else {dir1=dir1.Parent; dir2=dir2.Parent;} return dir1==dir2; – Eamon Nerbonne Feb 18 '10 at 13:12
  • 1
    Yeah case sensitivity is a difficult one because the OP wanted code to work on both Mono and Windows, but on Linux 2 names of different case are considered different, but on Windows they're considered to be the same file, so it's a per-platform decision. – Andy Shellam Feb 18 '10 at 14:41
1

You can use Minimatch, a port of 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 the AllowWindowsPaths = true option is necessary:

On Windows-style paths Minimatch's syntax was designed for Linux-style paths (with forward slashes only). In particular, it uses the backslash as an escape character, so it cannot simply accept Windows-style paths. My C# version preserves this behavior.

To suppress this, and allow both backslashes and forward slashes as path separators (in patterns or input), set the AllowWindowsPaths option:

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

Passing this option will disable escape characters entirely.

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

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

vulcan raven
  • 32,612
  • 11
  • 57
  • 93
0
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;
        }
    }
}
mafu
  • 31,798
  • 42
  • 154
  • 247
Denis
  • 833
  • 3
  • 12
  • 22
0

I used recursion to solve this problem for myself.

 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;
    }
}

Note: new FileInfo(path) returns a valid FileInfo even if path is not a file (the name field is equal to the directory name)

Hen3
  • 175
  • 8
0

Thank you, @Andy Shellam and @VladV and @Eamon Nerbonne

I found the other solution:

        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;
            }
        }

It can work well.

        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();
        }
lindexi
  • 4,182
  • 3
  • 19
  • 65
-1
bool equals = myDirectoryInfo1.FullName == myDirectoryInfo2.FullName;

?

herzmeister
  • 11,101
  • 2
  • 41
  • 51
  • 2
    This is the easy version of Binary Worrier's solution. Please note there is a problem however with the trailing slash: "c:\temp" is unequal to "c:\temp\". – Steven Feb 17 '10 at 14:51
  • 1
    Ok, so if I normalize out the trailing slashes and potentially the casing - and accept the fact that it does some FileIOPermission stuff - this looks like a good start, thanks! – Eamon Nerbonne Feb 17 '10 at 15:36
-1
bool Equals(string path1, string path2)
{
    return new Uri(path1) == new Uri(path2);
}

Uri constructor normalizes the path.

boris
  • 7
  • 2