¿Cómo comparar 2 archivos rápidamente usando .NET?

Resuelto Robin Rodricks asked hace 15 años • 21 respuestas

Los enfoques típicos recomiendan leer el binario a través de FileStream y compararlo byte por byte.

  • ¿Sería más rápida una comparación de suma de comprobación como CRC?
  • ¿Existen bibliotecas .NET que puedan generar una suma de comprobación para un archivo?
Robin Rodricks avatar Sep 01 '09 00:09 Robin Rodricks
Aceptado

El método más lento posible es comparar dos archivos byte por byte. Lo más rápido que se me ocurrió es una comparación similar, pero en lugar de un byte a la vez, se usaría una matriz de bytes de tamaño Int64 y luego se compararían los números resultantes.

Esto es lo que se me ocurrió:

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

En mis pruebas, pude ver que esto supera un escenario sencillo de ReadByte() en casi 3:1. Con un promedio de más de 1000 ejecuciones, obtuve este método a 1063 ms y el método siguiente (comparación directa byte a byte) a 3031 ms. El hash siempre regresaba por debajo del segundo, con un promedio de alrededor de 865 ms. Esta prueba se realizó con un archivo de video de ~100 MB.

Aquí están los métodos ReadByte y hash que utilicé, con fines de comparación:

    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;
    }
chsh avatar Aug 31 '2009 23:08 chsh

Lo más probable es que una comparación de suma de comprobación sea más lenta que una comparación byte por byte.

Para generar una suma de verificación, deberá cargar cada byte del archivo y realizar el procesamiento en él. Luego tendrás que hacer esto en el segundo archivo. Es casi seguro que el procesamiento será más lento que la verificación comparativa.

En cuanto a generar una suma de comprobación: puedes hacerlo fácilmente con las clases de criptografía. A continuación se muestra un breve ejemplo de cómo generar una suma de comprobación MD5 con C#.

Sin embargo, una suma de verificación puede ser más rápida y tener más sentido si se puede calcular previamente la suma de verificación del caso "de prueba" o "base". Si tiene un archivo existente y está comprobando si un archivo nuevo es el mismo que el existente, calcular previamente la suma de verificación en su archivo "existente" significaría que solo necesitaría realizar el DiskIO una vez, al mismo tiempo. archivo nuevo. Probablemente esto sería más rápido que una comparación byte por byte.

Reed Copsey avatar Aug 31 '2009 17:08 Reed Copsey