13

I have 2 DirectoryInfo objects and want to check if they are pointing to the same directory. Besides comparing their Fullname, are there any other better ways to do this? Please disregard the case of links.

Here's what I have.

DirectoryInfo di1 = new DirectoryInfo(@"c:\temp");
DirectoryInfo di2 = new DirectoryInfo(@"C:\TEMP");

if (di1.FullName.ToUpperInvariant() == di2.FullName.ToUpperInvariant())
{ // they are the same
   ...   
}

Thanks.

tranmq
  • 14,298
  • 2
  • 30
  • 27
  • All of the answers below will give incorrect results in certain cases, i.e. **they are all wrong**. See https://stackoverflow.com/a/39399232/1082063 for a correct answer. – David I. McIntosh Mar 30 '18 at 20:58

6 Answers6

6

Under Linux you could compare the INode numbers of the two files wheather they are identical. But under Windows there is no such concept, at least not that I know off. You would need to use p/invoke to resolve the links if any.

Comparing strings is the best you can do. Note that using String.Compare(str1, str2, StringComparison.OrdinalIgnoreCase) is faster than your approach of ToUpperInvariant() as it doesn't allocate new strings on the heap and won't suffer problems inherent in using a linguistic text comparison algorithm to compare file paths.

Dai
  • 126,861
  • 25
  • 221
  • 322
codymanix
  • 27,216
  • 19
  • 88
  • 147
  • What's wrong with this answered? Why was it marked down? I find the tip of using String.Compare() good. – tranmq Nov 25 '09 at 02:46
  • 1
    This is basically the same as the solution provided in the question. The better approach is to use Uri, which will handle different formatting, etc. – Matthew Cole Apr 19 '13 at 15:41
  • "But under Windows there is no such concept": This is wrong, hence this answer is essentially wrong. While INode in NFS is more reliable (guaranteed, in fact), windows can be coerced in to giving you similar information. See this answer in a related post: http://stackoverflow.com/a/39399232/1082063. – David I. McIntosh Oct 12 '16 at 03:19
  • 1
    If you are looking to determine if two directory info objects refer to the same directory, this will not do the trick. It should be pointed out that it is incredibly difficult to determine if two path names refer to the same file system object, given the possibility of junctions, links, shares on networks, etc. – David I. McIntosh Oct 12 '16 at 03:30
  • `InvariantCultureIgnoreCase` should generally be avoided - always prefer `OrdinalIgnoreCase` instead, especially in filesystems as file-names are not linguistic strings, see https://docs.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings – Dai Jan 20 '21 at 05:28
6

You can use Uri objects instead. However, your Uri objects must point to a "file" inside these directories. That file doesn't actually have to exist.

    private void CompareStrings()
    {
        string path1 = @"c:\test\rootpath";
        string path2 = @"C:\TEST\..\TEST\ROOTPATH";
        string path3 = @"C:\TeSt\RoOtPaTh\";

        string file1 = Path.Combine(path1, "log.txt");
        string file2 = Path.Combine(path2, "log.txt");
        string file3 = Path.Combine(path3, "log.txt");

        Uri u1 = new Uri(file1);
        Uri u2 = new Uri(file2);
        Uri u3 = new Uri(file3);

        Trace.WriteLine(string.Format("u1 == u2 ? {0}", u1 == u2));
        Trace.WriteLine(string.Format("u2 == u3 ? {0}", u2 == u3));

    }

This will print out:

u1 == u2 ? True
u2 == u3 ? True
Matthew Cole
  • 1,319
  • 2
  • 18
  • 30
  • This should actually be the answer as it covers edge-cases like "\..\" and is more robust, although it needs a little workaround – Florian K Mar 03 '17 at 11:46
  • 1
    but there is one problem, it translates stuff like %51 into letters instead of leaving them, so if you try this paths: `@"c:\test\rootQpath" @"C:\TEST\..\TEST\ROOT%51PATH"` It will return true – Florian K Mar 03 '17 at 12:02
  • While `C:\ ` will work, this will throw a `UriFormatException` if path is in the form of `C:` (ommiting the `\ `) as `Path.Combine` will result in something like this `C:log.txt` – Petaflop Jan 22 '18 at 12:20
5

Since netstandard2.1 there is finally an almost convenient and platform-independent way to check this: Path.GetRelativePath().

var areEqual = Path.GetRelativePath(path1, path2) == ".";

Works with absolute and relative paths. Also handles cases like foo/../foo/bar == foo/bar correctly.

Good Night Nerd Pride
  • 7,008
  • 4
  • 47
  • 60
  • 1
    I now see you have a deleted answer on the other question because people don't like duplicate answers; but anyhow - this is certainly viable! – Eamon Nerbonne Apr 04 '21 at 12:54
2

Inspired from here:

static public bool SameDirectory(string path1, string path2)
{
    return (
        0 == String.Compare(
            System.IO.Path.GetFullPath(path1).TrimEnd('\\'),
            System.IO.Path.GetFullPath(path2).TrimEnd('\\'),
            StringComparison.InvariantCultureIgnoreCase))
        ;
}    

Works for file too...

(BTW theoretically questions are duplicate, but this is the original and the other one is the most answered one...)

HTH

Community
  • 1
  • 1
Hertzel Guinness
  • 5,782
  • 3
  • 36
  • 43
0

Some extension methods that I wrote for a recent project includes one that will do it:

    public static bool IsSame(this DirectoryInfo that, DirectoryInfo other)
    {
        // zip extension wouldn't work here because it truncates the longer 
        // enumerable, resulting in false positive

        var e1 = that.EnumeratePathDirectories().GetEnumerator();
        var e2 = other.EnumeratePathDirectories().GetEnumerator();

        while (true)
        {
            var m1 = e1.MoveNext();
            var m2 = e2.MoveNext();
            if (m1 != m2) return false; // not same length
            if (!m1) return true; // finished enumerating with no differences found

            if (!e1.Current.Name.Trim().Equals(e2.Current.Name.Trim(), StringComparison.InvariantCultureIgnoreCase))
                return false; // current folder in paths differ
        }
    }

    public static IEnumerable<DirectoryInfo> EnumeratePathDirectories(this DirectoryInfo di)
    {
        var stack = new Stack<DirectoryInfo>();

        DirectoryInfo current = di;

        while (current != null)
        {
            stack.Push(current);
            current = current.Parent;
        }

        return stack;
    }

    // irrelevant for this question, but still useful:

    public static bool IsSame(this FileInfo that, FileInfo other)
    {
        return that.Name.Trim().Equals(other.Name.Trim(), StringComparison.InvariantCultureIgnoreCase) &&
               that.Directory.IsSame(other.Directory);
    }

    public static IEnumerable<DirectoryInfo> EnumeratePathDirectories(this FileInfo fi)
    {
        return fi.Directory.EnumeratePathDirectories();
    }

    public static bool StartsWith(this FileInfo fi, DirectoryInfo directory)
    {
        return fi.Directory.StartsWith(directory);
    }

    public static bool StartsWith(this DirectoryInfo di, DirectoryInfo directory)
    {
        return di.EnumeratePathDirectories().Any(d => d.IsSame(directory));
    }
Ronnie Overby
  • 43,601
  • 70
  • 265
  • 343
0

Case-insensitive comparison is the best you can get. Extract it to a helper class just in case mankind comes up with a better method.

public static class DirectoryInfoExtensions
{
    public static bool IsEqualTo(this DirectoryInfo left, DirectoryInfo right)
    {
        return left.FullName.ToUpperInvariant() == right.FullName.ToUpperInvariant();
    }
}

and use:

if (di1.IsEqualTo(di2))
{
    // Code here
}
Konstantin Spirin
  • 19,394
  • 14
  • 66
  • 88