【问题标题】:How can I determine that several file paths formats point to the same physical location [duplicate]如何确定多个文件路径格式指向同一个物理位置 [重复]
【发布时间】:2011-06-20 22:27:14
【问题描述】:

可能重复:
Best way to determine if two path reference to same file in C#

有几种方法可以指定目录位置。

例如:
\\machineName\c$\rootPath\subPath
\\machineName\shareName(共享指向 subPath)
C:\rootPath\subPath
subPath(如果已经在 C:\rootPath 中,则为相对路径

我需要确定所有这些路径彼此“相等”(实际上是硬盘上相同的物理位置)。

有什么办法可以在 C# 中做到这一点?

【问题讨论】:

  • 解析相对路径,可以使用System.IO.Path.GetFullPath

标签: c# path directory


【解决方案1】:

在 .NET 中没有本地方法可以做到这一点 - 它的级别太低了。

您也许可以使用 windows API 来实现这一点(查看目录 inode 或其他标识符),但我不知道哪个 API 会公开这一点。

【讨论】:

  • AFAIK Windows 中没有 inode。
  • @Al - 我真的不知道是否有,这就是我说“或其他标识符”的原因。
  • FAT,例如,除了其名称之外,没有唯一的文件标识符。谁知道通过网络可以获得什么样的奇怪文件系统。
【解决方案2】:

AKAIK 您甚至可以将同一个驱动器映射到多个驱动器号或子目录。网络共享目录也可以成倍增加,你永远不知道它们是否相同。

也许你可以添加信息为什么你需要知道它。

【讨论】:

    【解决方案3】:

    正如 Oded 所说,这在 .Net 中很难做到。您可以通过将具有长随机生成的文件名的文件写入该位置然后查看是否可以从其他位置看到它来作弊(取决于您的确切要求和权限等)。有点破解,但我认为这是一个非常合理的测试,而不是依赖于解析映射驱动器等。

    好吧,为 VB 道歉 - 这就是我在这台小上网本上的全部内容...... C# 不会有太大的不同......

    用法例如

    If sameLocation("\\machineName\c$\rootPath\subPath","\\machineName\shareName") Then...

    Public Function sameLocation(ByVal sPath1 As String, ByVal sPath2 As String) As TriState
        Dim sFile As String = randomFilename()
        Dim sFullPath1 As String = sPath1 & "\" & sFile
        Dim sFullPath2 As String = sPath2 & "\" & sFile
        Dim bReturn As Boolean = False
        Try
            Dim fs As New FileStream(sFullPath1, FileMode.CreateNew)
            fs.Close()
        Catch ex As Exception
            Return TriState.UseDefault
        End Try
    
        Try
            bReturn = File.Exists(sFullPath2)
        Catch ex As Exception
            Return TriState.UseDefault
        End Try
    
        File.Delete(sFullPath1)
        Return bReturn
    End Function
    
    Public Function randomFilename() As String
        Dim r As New Random
        Randomize(My.Computer.Clock.TickCount)
        Dim sb As New StringBuilder
        Dim chars As Int16 = 100
        While chars > 0
            chars -= 1
            sb.Append(Chr(r.Next(26) + 65))
        End While
        Return sb.ToString
    End Function
    

    您可以添加更多安全性,即读取时间戳等...

    【讨论】:

    • 如果您的程序和正在运行的用户有权读取/写入这些文件夹,这可能是您能找到的最佳答案。
    • 如果文件名本身是别名,您将永远不会知道这种方式。仅当别名位于路径中的某个位置时。
    • 投反对票时请离开cmets!!!
    【解决方案4】:

    您需要使用 GetFileInformationByHandle。
    查看 StackOverflow 中的 answerMSDN 帮助。

    这是我编写的适用于目录的方法:

    using System;
    using System.Runtime.InteropServices;
    
    namespace CompareByPath    
    {    
      public static class DirectoryHelper  
      {
        // all user defined types copied from 
        // http://pinvoke.net/default.aspx/kernel32.CreateFile
        // http://pinvoke.net/default.aspx/kernel32.GetFileInformationByHandle
        // http://pinvoke.net/default.aspx/kernel32.CloseHandle
    
        public const short INVALID_HANDLE_VALUE = -1;
    
        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;
        }
    
        [Flags]
        public enum EFileAccess : uint
        {
          GenericRead = 0x80000000,
          GenericWrite = 0x40000000,
          GenericExecute = 0x20000000,
          GenericAll = 0x10000000
        }
    
        [Flags]
        public enum EFileShare : uint
        {
          None = 0x00000000,
          Read = 0x00000001,
          Write = 0x00000002,
          Delete = 0x00000004
        }
    
        [Flags]
        public enum EFileAttributes : uint
        {
          Readonly = 0x00000001,
          Hidden = 0x00000002,
          System = 0x00000004,
          Directory = 0x00000010,
          Archive = 0x00000020,
          Device = 0x00000040,
          Normal = 0x00000080,
          Temporary = 0x00000100,
          SparseFile = 0x00000200,
          ReparsePoint = 0x00000400,
          Compressed = 0x00000800,
          Offline = 0x00001000,
          NotContentIndexed = 0x00002000,
          Encrypted = 0x00004000,
          Write_Through = 0x80000000,
          Overlapped = 0x40000000,
          NoBuffering = 0x20000000,
          RandomAccess = 0x10000000,
          SequentialScan = 0x08000000,
          DeleteOnClose = 0x04000000,
          BackupSemantics = 0x02000000,
          PosixSemantics = 0x01000000,
          OpenReparsePoint = 0x00200000,
          OpenNoRecall = 0x00100000,
          FirstPipeInstance = 0x00080000
        }
    
        public enum ECreationDisposition : uint
        {
          New = 1,
          CreateAlways = 2,
          OpenExisting = 3,
          OpenAlways = 4,
          TruncateExisting = 5
        }
    
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool GetFileInformationByHandle(IntPtr hFile, out        BY_HANDLE_FILE_INFORMATION lpFileInformation);
    
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        static extern IntPtr CreateFile(String lpFileName, UInt32 dwDesiredAccess, UInt32 dwShareMode, IntPtr lpSecurityAttributes, UInt32 dwCreationDisposition, UInt32 dwFlagsAndAttributes, IntPtr hTemplateFile);
    
        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool CloseHandle(IntPtr hObject);
    
        public static bool CompareDirectories(string d1, string d2)
        {
          bool result = false;
    
          BY_HANDLE_FILE_INFORMATION info1;
          BY_HANDLE_FILE_INFORMATION info2;
    
          IntPtr fileHandle1 = CreateFile(d1, (uint)EFileAccess.GenericRead, (uint)EFileShare.Read, IntPtr.Zero, (uint)ECreationDisposition.OpenExisting, (uint)(EFileAttributes.Directory | EFileAttributes.BackupSemantics), IntPtr.Zero);
          if (fileHandle1.ToInt32() != INVALID_HANDLE_VALUE)
          {
            bool rc = GetFileInformationByHandle(fileHandle1, out info1);
            if ( rc )
            {
              IntPtr fileHandle2 = CreateFile(d2, (uint)EFileAccess.GenericRead, (uint)EFileShare.Read, IntPtr.Zero, (uint)ECreationDisposition.OpenExisting, (uint)(EFileAttributes.Directory | EFileAttributes.BackupSemantics), IntPtr.Zero);
              if (fileHandle2.ToInt32() != INVALID_HANDLE_VALUE)
              {
                rc = GetFileInformationByHandle(fileHandle2, out info2);
                if ( rc )
                {
                  if (( info1.FileIndexHigh == info2.FileIndexHigh) &&
                      ( info1.FileIndexLow == info2.FileIndexLow) &&
                      ( info1.VolumeSerialNumber == info2.VolumeSerialNumber))
                  {
                    result = true;
                  }
                }
              }
    
              CloseHandle(fileHandle2);
            }
          }
    
          CloseHandle(fileHandle1);
    
          return result;
        }
      }
    }
    

    【讨论】:

      【解决方案5】:

      好问题,可能没有优雅的答案。

      我能找到的最佳指示是net share 命令行语句。如果您可以通过编程方式捕获和消化此命令生成的文本,则可以将网络共享 1:1 映射到其本地目标文件夹。然后,您可以在给定路径中查找这些共享映射的实例,并在执行基本字符串比较之前将它们替换为本地文件夹等效项。

      【讨论】:

        【解决方案6】:

        Windows 中有很多别名方案:

        • 短名称与长名称
        • 符号链接和硬链接
        • UNC 名称与映射驱动器
        • 多个映射驱动器到同一网络路径
        • d:\folder\ 路径与 \?\d\folder\ 路径
        • NTFS 挂载点

        其中任何一个都可以出现在目录树的任何级别。在 .NET 中,您可以解决其中一些问题,但不能解决其他问题。

        作为一种解决方法,请尝试锁定/解锁。将文件锁定为名称1,尝试以名称2打开,确保它失败。然后解锁,再次尝试打开,确保成功。等等,几次以避免误报。与 El Ronnoco 的方式不同,这种方式同时检测路径级和文件级别名。

        在某些网络文件系统上,可能根本不支持锁定。此外,这可能需要一段时间 - 每个锁定/解锁/打开操作都是一次网络往返。

        但这真的取决于您的要求。如果您只需要处理短/长名称,那就太过分了。

        编辑:如果文件是只读文件或已被其他人打开,则会出现额外的复杂情况。

        【讨论】:

        • +1 我喜欢这个答案,但我不确定我的方法破坏现有内容是什么意思?我的方法是创建一个空文件。
        • @El Ronnoco:误解了,抱歉。不过,您的方法不会检测到文件名级别的别名。
        • @Seva 没关系,我什至不知道那是什么! :)
        • @El Ronnoco:如果你有 Windows Vista 或 7,试试这个。创建一个名为 c:\a.txt 的文件。在里面放一些文字。然后在命令行上运行以下命令:mklink c:\b.txt c:\a.txt。然后打开文件c:\b.txt,欣赏一下。您的“在同一路径中创建随机文件”方法永远不会发现这两个是同一个文件。对于更难的版本,请尝试mklink /H
        • @Seva 有趣,我不知道。尽管 OP 在他的问题中确实表明他对 目录位置 感兴趣,而不是实际文件。显然,将随机数据写入文件将是一种疯狂的方法。应该有什么办法吧。是否有可以修改然后检查的文件系统文件属性?
        猜你喜欢
        • 1970-01-01
        • 2012-10-29
        • 2013-03-17
        • 2014-07-13
        • 2012-07-12
        • 2016-04-18
        • 2010-12-24
        • 1970-01-01
        • 2018-08-29
        相关资源
        最近更新 更多