确定两个路径引用C#中同一文件的最佳方法

时间:2009-01-04 09:05:02

标签: c# api filesystems

在即将发布的Java7中,有new API来检查两个文件对象是否是同一个文件引用。

.NET框架中是否提供了类似的API?

我通过MSDN搜索它,但没有任何启发我。

我希望它很简单,但我不想通过文件名进行比较,这将导致硬/符号链接和不同路径样式的问题。 (例如\\?\C:\C:\)。

我要做的就是阻止重复文件被拖放到我的链接列表中。

7 个答案:

答案 0 :(得分:33)

据我所知,(1) (2) (3) (4),JDK7的工作方式是在文件上调用GetFileInformationByHandle并比较dwVolumeSerialNumber ,nFileIndexHigh和nFileIndexLow。

每个MSDN:

  

您可以比较BY_HANDLE_FILE_INFORMATION结构中返回的VolumeSerialNumber和FileIndex成员,以确定两条路径是否映射到同一目标;例如,您可以比较两个文件路径并确定它们是否映射到同一目录。

我不认为这个函数是由.NET包装的,所以你必须使用P/Invoke

它可能适用于网络文件,也可能不适用。根据MSDN:

  

根据操作系统的基础网络组件和连接到的服务器类型,GetFileInformationByHandle函数可能会失败,返回部分信息或给定文件的完整信息。

快速测试显示它在使用SMB / Samba连接的Linux系统上使用符号链接按预期工作(相同值),但是当使用指向不同的共享访问时,它无法检测到文件是否相同相同的文件(FileIndex是相同的,但VolumeSerialNumber不同)。

答案 1 :(得分:7)

修改:请注意,@Rasmus Faber提到了Win32 api中的GetFileInformationByHandle功能,这可以满足您的需求,检查并提升他的answer以获取更多信息信息。


我认为您需要一个操作系统功能来提供您想要的信息,否则无论您做什么都会产生一些漏报。

例如,这些是否指的是同一个文件?

  • \服务器\共享\路径\ FILENAME.TXT
  • \服务器\ d $ \ TEMP \路径\ FILENAME.TXT

我会检查你的列表中没有重复文件的重要性,然后尽一切努力。

话虽如此,Path类中有一个方法可以完成一些工作:Path.GetFullPath,根据现有结构,它至少会扩展到长名称的路径。之后你只需比较字符串。虽然它不会万无一失,并且在我的例子中不会处理上面的两个链接。

答案 2 :(得分:2)

答案:没有万无一失的方法可以与字符串基本路径进行比较,以确定它们是否指向同一个文件。

主要原因是看似无关的路径可以指向文件系统重定向(联结,符号链接等)完全相同的文件。例如

“d:\ TEMP \ foo.txt的” “c:\ othertemp \ foo.txt”

这些路径可能指向同一个文件。这种情况明确地消除了任何字符串比较功能,作为确定两个路径是否指向同一文件的基础。

下一级是比较OS文件信息。打开两个路径的文件并比较句柄信息。在Windows中,可以使用GetFileInformationByHandle完成。 Lucian Wischik在这里就这个问题做了很好的post

但这种方法仍然存在问题。仅当执行检查的用户帐户能够打开两个文件进行阅读时,它才有效。有许多项可以阻止用户打开一个或两个文件。包括但不限于......

  • 缺乏足够的文件权限
  • 对文件路径中的目录缺乏足够的权限
  • 在第一个文件打开和第二个文件之间发生的文件系统更改,例如网络断开。

当您开始查看所有这些问题时,您就会开始理解为什么Windows不提供确定两条路径是否相同的方法。这不是一个简单/可能的问题。

答案 3 :(得分:2)

以下是使用IsSameFile的{​​{1}}的C#实现:

NativeMethods.cs

GetFileInformationByHandle

PathUtility.cs

public static class NativeMethods
{
  [StructLayout(LayoutKind.Explicit)]
  public struct BY_HANDLE_FILE_INFORMATION
  {
    [FieldOffset(0)]
    public uint FileAttributes;

    [FieldOffset(4)]
    public FILETIME CreationTime;

    [FieldOffset(12)]
    public FILETIME LastAccessTime;

    [FieldOffset(20)]
    public FILETIME LastWriteTime;

    [FieldOffset(28)]
    public uint VolumeSerialNumber;

    [FieldOffset(32)]
    public uint FileSizeHigh;

    [FieldOffset(36)]
    public uint FileSizeLow;

    [FieldOffset(40)]
    public uint NumberOfLinks;

    [FieldOffset(44)]
    public uint FileIndexHigh;

    [FieldOffset(48)]
    public uint FileIndexLow;
  }

  [DllImport("kernel32.dll", SetLastError = true)]
  public static extern bool GetFileInformationByHandle(SafeFileHandle hFile, out BY_HANDLE_FILE_INFORMATION lpFileInformation);

  [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  public static extern SafeFileHandle CreateFile([MarshalAs(UnmanagedType.LPTStr)] string filename,
    [MarshalAs(UnmanagedType.U4)] FileAccess access,
    [MarshalAs(UnmanagedType.U4)] FileShare share,
    IntPtr securityAttributes,
    [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
    [MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes,
    IntPtr templateFile);
}

答案 4 :(得分:1)

首先我认为这很容易,但工作:

  string fileName1 = @"c:\vobp.log";
  string fileName2 = @"c:\vobp.log".ToUpper();
  FileInfo fileInfo1 = new FileInfo(fileName1);
  FileInfo fileInfo2 = new FileInfo(fileName2);

  if (!fileInfo1.Exists || !fileInfo2.Exists)
  {
    throw new Exception("one of the files does not exist");
  }

  if (fileInfo1.FullName == fileInfo2.FullName)
  {
    MessageBox.Show("equal"); 
  }

也许这个图书馆有助http://www.codeplex.com/FileDirectoryPath。我自己没有用过它。

编辑:在该网站上查看此示例:

  //
  // Path comparison
  //
  filePathAbsolute1 = new FilePathAbsolute(@"C:/Dir1\\File.txt");
  filePathAbsolute2 = new FilePathAbsolute(@"C:\DIR1\FILE.TXT");
  Debug.Assert(filePathAbsolute1.Equals(filePathAbsolute2));
  Debug.Assert(filePathAbsolute1 == filePathAbsolute2);

答案 5 :(得分:0)

如果您需要反复比较相同的文件名,我建议您考虑对这些名称进行元素化。

在Unix系统下,有realpath()功能可以使您的路径成为可能。我认为如果你有一个复杂的路径,这通常是最好的选择。但是,通过网络连接挂载的卷可能会失败。

但是,基于realpath()方法,如果要支持包括网络卷在内的多个卷,可以编写自己的函数来检查路径中的每个目录名称,如果它引用了卷,则确定是否为卷引用两条路径都是一样的。这就是说,挂载点可能不同(即目标卷上的路径可能不是该卷的根),因此沿途解决所有问题并不容易,但它确实是可能的(否则如何它会起作用吗?!)

一旦文件名正确地进行了内省化,简单的字符串比较就会给出正确答案。

如果您不需要一遍又一遍地比较相同的文件名,那么Rasmus答案可能是最快的方法。

答案 6 :(得分:-4)

您始终可以对两者执行MD5编码并比较结果。效率不高,但比手动比较文件更容易。

以下是how to MD5 a string in C#上的帖子。