C#에서 동일한 파일에 대한 두 경로를 참조하는지 확인하는 가장 좋은 방법

StackOverflow https://stackoverflow.com/questions/410705

  •  03-07-2019
  •  | 
  •  

문제

다가오는 Java7에는 a가 있습니다 새로운 API 두 파일 객체가 동일한 파일 참조인지 확인하려면

.NET 프레임 워크에 유사한 API가 제공됩니까?

나는 MSDN을 통해 그것을 검색하지만 아무것도 밝히지 않습니다.

나는 그것을 간단하게 원하지만 파일 이름으로 비교하고 싶지 않아 하드/상징적 링크와 다양한 스타일의 경로에 문제가 생길 것입니다. (예 : \\?\C:\, C:\).

내가해야 할 일은 복제 된 파일이 드래그하고 링크리스트로 떨어지는 것을 방지하는 것입니다.

도움이 되었습니까?

해결책

최대한 멀리 볼 수 (1) (2) (3) (4), JDK7이하는 방식은 getFileInformationByHandle 파일에서 dwvolumeserialnumber, nfileindexhigh 및 nfileindexlow를 비교합니다.

MSDN에 따라 :

BY_HANDLE_FILE_INFORMATION 구조에서 반환 된 VolumeSerialNumber 및 FileIndex 멤버를 비교하여 두 경로가 동일한 대상에 맵핑되는지 확인할 수 있습니다. 예를 들어, 두 파일 경로를 비교하고 동일한 디렉토리에 매핑되는지 확인할 수 있습니다.

이 기능은 .NET에 의해 래핑된다고 생각하지 않으므로 사용해야합니다. p/호출.

네트워크 파일에서 작동하거나 작동하지 않을 수도 있습니다. MSDN에 따르면 :

운영 체제의 기본 네트워크 구성 요소와 연결된 서버 유형에 따라 GetFileInformationByHandle 기능이 실패하거나 부분 정보를 반환하거나 주어진 파일에 대한 전체 정보가 발생할 수 있습니다.

빠른 테스트는 SMB/SAMBA를 사용하여 연결된 Linux 시스템의 상징적 링크와 함께 예상 (동일한 값)으로 작동하지만 동일한 파일을 가리키는 다른 주식을 사용하여 액세스 할 때 파일이 동일하다는 것을 감지 할 수 없음을 보여줍니다. FileIndex는 동일하지만 VolumeserialNumber는 다릅니다).

다른 팁

편집하다: 주목하십시오 @Rasmus Faber 언급 getFileInformationByHandle Win32 API에서 기능하면, 이것은 당신이 원하는 일을하고, 그의 대답 자세한 내용은.


원하는 정보를 제공하기 위해 OS 기능이 필요하다고 생각합니다. 그렇지 않으면 귀하가 무엇을하든 잘못된 부정적인 정보를 얻을 수 있습니다.

예를 들어, 이것들은 같은 파일을 참조합니까?

  • server share path filename.txt
  • server d $ temp path filename.txt

목록에 중복 파일이없는 것이 얼마나 중요한지 조사한 다음 최선의 노력을 기울일 것입니다.

그러나 Path 클래스에는 일부 작업을 수행 할 수있는 방법이 있습니다. 경로 .getfullpath, 기존 구조에 따라 최소한 긴 이름으로 경로를 확장합니다. 그 후에는 문자열을 비교합니다. 그래도 완벽하지는 않으며 내 예에서 위의 두 링크를 처리하지 않습니다.

답변 : 문자열 기본 경로와 비교하여 동일한 파일을 가리키는 지 여부를 결정할 수있는 완벽한 방법이 없습니다.

주된 이유는 겉보기에 관련되지 않은 경로가 정확히 동일한 파일을 파일 시스템 리디렉션 (접점, 상징적 링크 등)을 가리킬 수 있기 때문입니다. 예를 들어

"D : temp foo.txt" "C : OtherTemp foo.txt"

이러한 경로는 잠재적으로 동일한 파일을 가리킬 수 있습니다. 이 사례는 두 개의 경로가 동일한 파일을 가리키는 지 여부를 결정하기위한 기초로 문자열 비교 함수를 명확하게 제거합니다.

다음 단계는 OS 파일 정보를 비교하는 것입니다. 두 가지 경로의 파일을 열고 핸들 정보를 비교하십시오. Windows에서는 GetFileInformationByHandle로 수행 할 수 있습니다. Lucian Wischik은 우수했습니다 게시하다 이 주제에서 여기.

그래도이 접근법에는 여전히 문제가 있습니다. 수표를 수행하는 사용자 계정이 읽기를 위해 두 파일을 모두 열 수있는 경우에만 작동합니다. 사용자가 하나 또는 두 파일을 모두 열지 못하게하는 수많은 항목이 있습니다. 포함하지만 이에 국한되지 않습니다.

  • 파일에 대한 충분한 권한이 부족합니다
  • 파일 경로에서 디렉토리에 대한 충분한 권한 부족
  • 파일 시스템 변경 첫 번째 파일의 시작과 네트워크 연결이 끊김과 같은 두 번째 파일 사이에 발생합니다.

이 모든 문제를보기 시작하면 Windows가 두 경로가 동일인지 판단하는 방법을 제공하지 않는 이유를 이해하기 시작합니다. 대답하기 쉬운/가능한 질문은 아닙니다.

다음은 C# 구현입니다 IsSameFile 사용 GetFileInformationByHandle:

nativemethods.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);
}

Pathutility.cs

public static bool IsSameFile(string path1, string path2)
{
  using (SafeFileHandle sfh1 = NativeMethods.CreateFile(path1, FileAccess.Read, FileShare.ReadWrite, 
      IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero))
  {
    if (sfh1.IsInvalid)
      Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());

    using (SafeFileHandle sfh2 = NativeMethods.CreateFile(path2, FileAccess.Read, FileShare.ReadWrite,
      IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero))
    {
      if (sfh2.IsInvalid)
        Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());

      NativeMethods.BY_HANDLE_FILE_INFORMATION fileInfo1;
      bool result1 = NativeMethods.GetFileInformationByHandle(sfh1, out fileInfo1);
      if (!result1)
        throw new IOException(string.Format("GetFileInformationByHandle has failed on {0}", path1));

      NativeMethods.BY_HANDLE_FILE_INFORMATION fileInfo2;
      bool result2 = NativeMethods.GetFileInformationByHandle(sfh2, out fileInfo2);
      if (!result2)
        throw new IOException(string.Format("GetFileInformationByHandle has failed on {0}", path2));

      return fileInfo1.VolumeSerialNumber == fileInfo2.VolumeSerialNumber
        && fileInfo1.FileIndexHigh == fileInfo2.FileIndexHigh
        && fileInfo1.FileIndexLow == fileInfo2.FileIndexLow;
    }
  }
}

먼저 나는 그것이 정말 쉽다고 생각했지만 이것 그렇지 않습니다 일하다:

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

동일한 파일 이름을 반복해서 비교 해야하는 경우 해당 이름을 Canalaling을 살펴 보는 것이 좋습니다.

유닉스 시스템 아래에 있습니다 RealPath () 경로를 할 수있는 기능. 나는 그것이 당신이 가지고 있다면 그것이 일반적으로 최선의 방법이라고 생각합니다. 복잡한 길. 그러나 네트워크 연결을 통해 장착 된 볼륨에서 실패 할 가능성이 높습니다.

그러나 RealPath () 접근 방식을 기반으로 네트워크 볼륨을 포함한 여러 볼륨을 지원하려면 경로에서 각 디렉토리 이름을 확인하는 자체 기능을 작성하고 볼륨을 참조하는 경우 두 경로에서 볼륨 참조가 있는지 확인할 수 있습니다. 는 똑같은. 이것은 마운트 포인트가 다를 수 있습니다 (즉, 대상 볼륨의 경로는 해당 볼륨의 근본이 아닐 수 있습니다). 처음에 작동할까요?!)

파일 이름이 올바르게 연합되면 간단한 문자열 비교를 통해 정답을 제공합니다.

Rasmus 답변은 동일한 파일 이름을 반복해서 비교할 필요가 없다면 아마도 가장 빠른 방법 일 것입니다.

항상 둘 다에서 MD5 엔코드를 수행하고 결과를 비교할 수 있습니다. 정확히 효율적이지는 않지만 파일을 직접 비교하는 것보다 쉽습니다.

여기에 게시물이 있습니다 c#에서 문자열을 md5하는 방법.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top