.NET을 사용하여 2 개의 파일을 빠르게 비교하는 방법은 무엇입니까? 바이트 단위로 비교하는

일반적인 접근 방식 은 FileStream을 통해 바이너리를 읽고 바이트 단위로 비교하는 것이 좋습니다.

  • CRC와 같은 체크섬 비교가 더 빠릅니까?
  • 파일의 체크섬을 생성 할 수있는 .NET 라이브러리가 있습니까?


답변

체크섬 비교는 바이트 단위 비교보다 느릴 수 있습니다.

체크섬을 생성하려면 파일의 각 바이트를로드하고 처리해야합니다. 그런 다음 두 번째 파일에서이 작업을 수행해야합니다. 비교 검사보다 처리 속도가 거의 느려집니다.

체크섬 생성 : 암호화 클래스를 사용하여이 작업을 쉽게 수행 할 수 있습니다. 다음 은 C #으로 MD5 체크섬을 생성하는 간단한 예입니다 .

그러나 “테스트”또는 “기본”사례의 체크섬을 미리 계산할 수 있으면 체크섬이 더 빠르고 더 의미가있을 수 있습니다. 기존 파일이 있고 새 파일이 기존 파일과 동일한 지 확인하려는 경우 “기존”파일의 체크섬을 사전 계산하면 DiskIO를 한 번만 수행하면됩니다. 새로운 파일. 이것은 바이트 단위 비교보다 빠를 것입니다.


답변

가장 느린 방법은 두 파일을 바이트 단위로 비교하는 것입니다. 내가 찾은 가장 빠른 것은 비슷한 비교이지만 한 번에 1 바이트 대신 Int64 크기의 바이트 배열을 사용하고 결과 숫자를 비교합니다.

내가 생각해 낸 것은 다음과 같습니다.

    const int BYTES_TO_READ = sizeof(Int64);

    static bool FilesAreEqual(FileInfo first, FileInfo second)
    {
        if (first.Length != second.Length)
            return false;

        if (string.Equals(first.FullName, second.FullName, StringComparison.OrdinalIgnoreCase))
            return true;

        int iterations = (int)Math.Ceiling((double)first.Length / BYTES_TO_READ);

        using (FileStream fs1 = first.OpenRead())
        using (FileStream fs2 = second.OpenRead())
        {
            byte[] one = new byte[BYTES_TO_READ];
            byte[] two = new byte[BYTES_TO_READ];

            for (int i = 0; i < iterations; i++)
            {
                 fs1.Read(one, 0, BYTES_TO_READ);
                 fs2.Read(two, 0, BYTES_TO_READ);

                if (BitConverter.ToInt64(one,0) != BitConverter.ToInt64(two,0))
                    return false;
            }
        }

        return true;
    }

필자의 테스트에서 이는 간단한 3 : 1만큼 간단한 ReadByte () 시나리오보다 성능이 뛰어남을 알 수있었습니다. 평균 1000 회 이상 실행되면 1063ms 에서이 방법을 얻었고 3031ms에서 아래의 방법 (바이트 단위로 빠른 비교)을 얻었습니다. 해싱은 항상 평균 865ms에서 1 초 미만으로 돌아 왔습니다. 이 테스트는 ~ 100MB 비디오 파일로 수행되었습니다.

비교 목적으로 사용한 ReadByte 및 해싱 방법은 다음과 같습니다.

    static bool FilesAreEqual_OneByte(FileInfo first, FileInfo second)
    {
        if (first.Length != second.Length)
            return false;

        if (string.Equals(first.FullName, second.FullName, StringComparison.OrdinalIgnoreCase))
            return true;

        using (FileStream fs1 = first.OpenRead())
        using (FileStream fs2 = second.OpenRead())
        {
            for (int i = 0; i < first.Length; i++)
            {
                if (fs1.ReadByte() != fs2.ReadByte())
                    return false;
            }
        }

        return true;
    }

    static bool FilesAreEqual_Hash(FileInfo first, FileInfo second)
    {
        byte[] firstHash = MD5.Create().ComputeHash(first.OpenRead());
        byte[] secondHash = MD5.Create().ComputeHash(second.OpenRead());

        for (int i=0; i<firstHash.Length; i++)
        {
            if (firstHash[i] != secondHash[i])
                return false;
        }
        return true;
    }

답변

만약 당신 진정으로 전체 바이트 단위 비교 가 필요하다고 결정한다면 (해싱에 대한 다른 답변을보십시오) 가장 쉬운 해결책은 다음과 같습니다 :

• 예를 System.IO.FileInfo들면 :

public static bool AreFileContentsEqual(FileInfo fi1, FileInfo fi2) =>
    fi1.Length == fi2.Length &&
    (fi1.Length == 0 || File.ReadAllBytes(fi1.FullName).SequenceEqual(
                        File.ReadAllBytes(fi2.FullName)));

System.String경로 이름 :

public static bool AreFileContentsEqual(String path1, String path2) =>
                   AreFileContentsEqual(new FileInfo(path1), new FileInfo(path2));

게시 된 다른 답변과 달리 이것은 바이너리, 텍스트, 미디어, 실행 파일 등 모든 종류의 파일에 대해 결정적으로 정확 하지만 완전한 바이너리 비교 로 “중요하지 않은”방법 다른 파일 (예 : BOM , 줄) -ending , 문자 인코딩 , 미디어 메타 데이터, 공백, 패딩, 소스 코드 주석 등은 항상 같지 않은 것으로 간주됩니다 .

이 코드는 두 파일을 모두 메모리에 완전히로드하므로 실제로 거대한 파일 을 비교하는 데 사용 해서는 안됩니다 . 중요주의를 넘어, 전체로드는 .NET의 디자인 주어진 페널티 킥 정말 아니다 GC는 (그것은 근본적으로 작은 유지하기 위해 최적화 있기 때문에 단명 할당 매우 싼 ), 때 사실도 최적이 될 수 파일 크기가 예상된다 이상이어야 85K (다음과 같이) 사용자의 최소한의 코드를 사용하여 최대한으로 파일 성능 문제를 위임하는 것을 의미하기 때문에, CLR, BCL, 및 JIT(예를 들어) 최신 설계 기술, 시스템 코드, 및 적응 런타임 최적화의 혜택을.

또한 이러한 임시 시나리오의 경우 LINQ열거자를 통해 바이트 단위 비교 성능에 대한 우려가 불분명합니다 ( 파일 I / O에 대해 디스크 ahitt̲ a̲l̲l̲ 를 때리면 몇 배씩 줄어듦). 다양한 메모리 비교 대안 중 하나. 예를 들어, 비록 SequenceEqual 않는 사실 우리의 “최적화”제공 첫 불일치에 포기를 이 거의 파일 ‘내용을 이미 가지고 가져온 후, 각 완전히 필요한 경기를 확인하기 위해 중요하지 ..


답변

리드 콥시 의 답변 외에도 :

  • 최악의 경우 두 파일이 동일합니다. 이 경우 파일을 바이트 단위로 비교하는 것이 가장 좋습니다.

  • 두 파일이 동일하지 않으면 파일이 동일하지 않다는 것을 빨리 감지하여 속도를 높일 수 있습니다.

예를 들어, 두 파일의 길이가 다른 경우 파일이 동일 할 수 없으며 실제 내용을 비교할 필요조차 없다는 것을 알 수 있습니다.


답변

작은 8 바이트 청크를 읽지 않고 더 큰 청크를 읽으면 루프를 돌리면 훨씬 빨라집니다. 평균 비교 시간을 1/4로 줄였습니다.

    public static bool FilesContentsAreEqual(FileInfo fileInfo1, FileInfo fileInfo2)
    {
        bool result;

        if (fileInfo1.Length != fileInfo2.Length)
        {
            result = false;
        }
        else
        {
            using (var file1 = fileInfo1.OpenRead())
            {
                using (var file2 = fileInfo2.OpenRead())
                {
                    result = StreamsContentsAreEqual(file1, file2);
                }
            }
        }

        return result;
    }

    private static bool StreamsContentsAreEqual(Stream stream1, Stream stream2)
    {
        const int bufferSize = 1024 * sizeof(Int64);
        var buffer1 = new byte[bufferSize];
        var buffer2 = new byte[bufferSize];

        while (true)
        {
            int count1 = stream1.Read(buffer1, 0, bufferSize);
            int count2 = stream2.Read(buffer2, 0, bufferSize);

            if (count1 != count2)
            {
                return false;
            }

            if (count1 == 0)
            {
                return true;
            }

            int iterations = (int)Math.Ceiling((double)count1 / sizeof(Int64));
            for (int i = 0; i < iterations; i++)
            {
                if (BitConverter.ToInt64(buffer1, i * sizeof(Int64)) != BitConverter.ToInt64(buffer2, i * sizeof(Int64)))
                {
                    return false;
                }
            }
        }
    }
}

답변

바이트 단위 비교보다 체크섬 비교가 약간 더 빠를 수있는 유일한 방법은 한 번에 하나의 파일을 읽고 디스크 헤드의 탐색 시간을 다소 단축한다는 것입니다. 그러나 약간의 이득은 해시 계산에 추가 된 시간에 의해 매우 잘 흡수 될 수 있습니다.

또한 파일의 체크섬 비교는 파일이 동일한 경우에만 더 빠를 가능성이 있습니다. 그렇지 않은 경우, 바이트 별 비교는 첫 번째 차이로 끝나서 훨씬 빠릅니다.

또한 해시 코드 비교 는 파일이 동일 할 가능성높다는 것을 알려줍니다 . 100 % 확실하게하려면 바이트 단위 비교를 수행해야합니다.

예를 들어 해시 코드가 32 비트 인 경우 해시 코드가 일치하면 파일이 동일한 지 약 99.99999998 %입니다. 100 %에 가깝지만 100 % 확실성이 필요한 경우에는 그렇지 않습니다.


답변

편집 : 이 방법 것 없다 이진 파일을 비교 !

.NET 4.0에서 File클래스에는 다음 두 가지 새로운 메소드가 있습니다.

public static IEnumerable<string> ReadLines(string path)
public static IEnumerable<string> ReadLines(string path, Encoding encoding)

다음을 사용할 수 있음을 의미합니다.

bool same = File.ReadLines(path1).SequenceEqual(File.ReadLines(path2));