views:

305

answers:

5

So I have two Windows filenames I need to compare to determine if they are the same. One the user gave me, one given to me by another program. So how should you compare:

C:\Program Files\Application1\APP.EXE
C:\Progra~1\Applic~1\APP.EXE
C:\program files\applic~1\app.exe

I can't seem to find a way to consistently 'normalize' the path, I tried using Path.GetFullPath(path) and new FileInfo(path).FullName and neither seem to resolve this.

UPDATE:

Path.GetFullPath(path) will correct the short to long name conversion but it will not normalize case. Thus a StringComparer.OrdinalIgnoreCase.Equals(path1, path2) is required.

+3  A: 

Harder than it ought to be. See http://stackoverflow.com/questions/684684 and http://stackoverflow.com/questions/562701.

Tim Sylvester
this question is tagged c#
tanascius
Win32 API functions are available in C#, at least on Windows platforms.
Tim Sylvester
Is there a .NET way of doing this without doing the function imports?
C. Ross
Yep, your solution certainly works, but a pure .net is preferred ... well, at least I`d prefer it ... :)
tanascius
This is probably the 'correct' solution, but more effort that this specific problem deserves. Since I'm not worried about shares, unc, uri, hard/soft links, etc... I'm just going to go with a case-insensitive compare of GetFullPath().
csharptest.net
+2  A: 

You will need Path.GetFullPath() + case insensitive string comparison.

Running the following code:

using System;
using System.IO;

class Test {
 static void Main ()
 {
  //string [] str = new string[] {@"c:\program files\vim\vim72", @"c:\progra~1\vim\vim72"};
  string [] str = new string[] {@"c:\program files\Refere~1\microsoft", @"c:\progra~1\Refere~1\microsoft"};
  foreach (string s in str) {
   // Call s = Environment.ExpandEnvironmentVariables (s) if needed.
   Console.WriteLine (Path.GetFullPath (s));
  }
 }
}

gives:

c:\program files\Reference Assemblies\microsoft
c:\Program Files\Reference Assemblies\microsoft
Gonzalo
Delete this quick, before you get hammered on the downvote...
Philip Wallace
It works for me here. I tried c:\Progra~1\Refere~1\Microsoft and c:\Program Files\Referenced Assemblies\microsoft...
Gonzalo
Apparently he tried it. I posted the same thing and got 3 downvotes instantly.
Philip Wallace
I added my test and its output to the answer, just in case. Thanks.
Gonzalo
@xaero You posted basically "try GetFullPath" when the question says "I tried GetFullPath". Gonzalo included the necessary case-insensitive comparison, which is likely why it didn't work for the OP, and example code.
Tim Sylvester
It's worth noting that `Path.GetFullPath` does not expand variables, so e.g., comparing `%PROGRAMFILES%\Reference Assemblies\Microsoft` to `c:\program files\Reference Assemblies\Microsoft` will not work.
Tim Sylvester
Easy fix. Let me add a note to the answer...
Gonzalo
@Tim - fair enough. It's OK anyway, I got a badge for deleting my post... ;)
Philip Wallace
Also, I haven't tried it, but `GetFullPath` probably doesn't handle the junctions or links supported by recent versions of NTFS. Probably not an issue for 99.9% of people, though.
Tim Sylvester
Yes, I thought about junctions and symbolic links in unix but didn't think it was worth mentioning.
Gonzalo
I use them fairly often but that's definitely not common, you basically have to use non-MS software to do it outside the command line, e.g.: http://schinagl.priv.at/nt/hardlinkshellext/hardlinkshellext.html
Tim Sylvester
This is the monster method that GetFullPath calls: http://paste-it.net/public/haa0ee9/
Philip Wallace
@Tim: in unix it would be readlink() and is part of the system C library.
Gonzalo
This is working for my limited case... The answers involving GetFileInformationByHandle are just too complex to deal with. Thx.
csharptest.net
A: 

a short test run says that the below code will work for the paths given:

bool CompareFileName(string file1, string file2)
        {
            var directory1 = Path.GetDirectoryName(file1);
            var directory2 = Path.GetDirectoryName(file2);
            var fileName1 = Path.GetFileName(file1);
            var fileName2 = Path.GetFileName(file2);

            return directory1.Equals(directory2, StringComparison.InvariantCultureIgnoreCase) &&
                   fileName1.Equals(fileName2, StringComparison.InvariantCultureIgnoreCase);
        }

this assumes windows platform (an assumption made due to the windows centric paths given as example paths)

Rune FS
A: 

It is simple for 99.9% of the cases:

public static bool PathsAreEquivalent(string path1, string path2)
{
    path1 = path1.ToLower();
    path2 = path2.ToLower();
    return(System.IO.Path.GetFullPath(path1) == System.IO.Path.GetFullPath(path2));
}

It doesn't take symbolic links into accound and might not work under Unix but it works for all the rest.

bitbonk
A: 

I use the FileInfo object. If you create a fileinfo object of a file that actually exists the Directory property gives a nicely formatted path name.

You also get the additional benefit of being able to test if the file actually exists.

JayG