c# get checksum of file
¿Cómo comparar rápidamente 2 archivos usando.NET? (18)
Además de la respuesta de Reed Copsey :
El peor caso es donde los dos archivos son idénticos. En este caso, es mejor comparar los archivos byte a byte.
Si, si los dos archivos no son idénticos, puede acelerar un poco las cosas detectando antes que no son idénticos.
Por ejemplo, si los dos archivos son de diferente longitud, entonces usted sabe que no pueden ser idénticos, y ni siquiera tiene que comparar su contenido real.
Los enfoques típicos recomiendan leer el binario vía FileStream y compararlo byte por byte.
- ¿Sería más rápida una comparación de suma de comprobación como CRC?
- ¿Hay alguna biblioteca .NET que pueda generar una suma de comprobación para un archivo?
Algo (con suerte) razonablemente eficiente:
public class FileCompare
{
public static bool FilesEqual(string fileName1, string fileName2)
{
return FilesEqual(new FileInfo(fileName1), new FileInfo(fileName2));
}
/// <summary>
///
/// </summary>
/// <param name="file1"></param>
/// <param name="file2"></param>
/// <param name="bufferSize">8kb seemed like a good default</param>
/// <returns></returns>
public static bool FilesEqual(FileInfo file1, FileInfo file2, int bufferSize = 8192)
{
if (!file1.Exists || !file2.Exists || file1.Length != file2.Length) return false;
var buffer1 = new byte[bufferSize];
var buffer2 = new byte[bufferSize];
using (var stream1 = file1.Open(FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (var stream2 = file2.Open(FileMode.Open, FileAccess.Read, FileShare.Read))
{
while (true)
{
var bytesRead1 = stream1.Read(buffer1, 0, bufferSize);
var bytesRead2 = stream2.Read(buffer2, 0, bufferSize);
if (bytesRead1 != bytesRead2) return false;
if (bytesRead1 == 0) return true;
if (!ArraysEqual(buffer1, buffer2, bytesRead1)) return false;
}
}
}
}
/// <summary>
///
/// </summary>
/// <param name="array1"></param>
/// <param name="array2"></param>
/// <param name="bytesToCompare"> 0 means compare entire arrays</param>
/// <returns></returns>
public static bool ArraysEqual(byte[] array1, byte[] array2, int bytesToCompare = 0)
{
if (array1.Length != array2.Length) return false;
var length = (bytesToCompare == 0) ? array1.Length : bytesToCompare;
var tailIdx = length - length % sizeof(Int64);
//check in 8 byte chunks
for (var i = 0; i < tailIdx; i += sizeof(Int64))
{
if (BitConverter.ToInt64(array1, i) != BitConverter.ToInt64(array2, i)) return false;
}
//check the remainder of the array, always shorter than 8 bytes
for (var i = tailIdx; i < length; i++)
{
if (array1[i] != array2[i]) return false;
}
return true;
}
}
Aquí hay algunas funciones de utilidad que le permiten determinar si dos archivos (o dos flujos) contienen datos idénticos.
He proporcionado una versión "rápida" que tiene varios subprocesos, ya que compara matrices de bytes (cada memoria tampón se llena de lo que se ha leído en cada archivo) en diferentes subprocesos utilizando Tareas.
Como se esperaba, es mucho más rápido (alrededor de 3 veces más rápido) pero consume más CPU (porque tiene varios hilos) y más memoria (porque necesita dos búferes de matriz de bytes por hilo de comparación).
public static bool AreFilesIdenticalFast(string path1, string path2)
{
return AreFilesIdentical(path1, path2, AreStreamsIdenticalFast);
}
public static bool AreFilesIdentical(string path1, string path2)
{
return AreFilesIdentical(path1, path2, AreStreamsIdentical);
}
public static bool AreFilesIdentical(string path1, string path2, Func<Stream, Stream, bool> areStreamsIdentical)
{
if (path1 == null)
throw new ArgumentNullException(nameof(path1));
if (path2 == null)
throw new ArgumentNullException(nameof(path2));
if (areStreamsIdentical == null)
throw new ArgumentNullException(nameof(path2));
if (!File.Exists(path1) || !File.Exists(path2))
return false;
using (var thisFile = new FileStream(path1, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (var valueFile = new FileStream(path2, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
if (valueFile.Length != thisFile.Length)
return false;
if (!areStreamsIdentical(thisFile, valueFile))
return false;
}
}
return true;
}
public static bool AreStreamsIdenticalFast(Stream stream1, Stream stream2)
{
if (stream1 == null)
throw new ArgumentNullException(nameof(stream1));
if (stream2 == null)
throw new ArgumentNullException(nameof(stream2));
const int bufsize = 80000; // 80000 is below LOH (85000)
var tasks = new List<Task<bool>>();
do
{
// consumes more memory (two buffers for each tasks)
var buffer1 = new byte[bufsize];
var buffer2 = new byte[bufsize];
int read1 = stream1.Read(buffer1, 0, buffer1.Length);
if (read1 == 0)
{
int read3 = stream2.Read(buffer2, 0, 1);
if (read3 != 0) // not eof
return false;
break;
}
// both stream read could return different counts
int read2 = 0;
do
{
int read3 = stream2.Read(buffer2, read2, read1 - read2);
if (read3 == 0)
return false;
read2 += read3;
}
while (read2 < read1);
// consumes more cpu
var task = Task.Run(() =>
{
return IsSame(buffer1, buffer2);
});
tasks.Add(task);
}
while (true);
Task.WaitAll(tasks.ToArray());
return !tasks.Any(t => !t.Result);
}
public static bool AreStreamsIdentical(Stream stream1, Stream stream2)
{
if (stream1 == null)
throw new ArgumentNullException(nameof(stream1));
if (stream2 == null)
throw new ArgumentNullException(nameof(stream2));
const int bufsize = 80000; // 80000 is below LOH (85000)
var buffer1 = new byte[bufsize];
var buffer2 = new byte[bufsize];
var tasks = new List<Task<bool>>();
do
{
int read1 = stream1.Read(buffer1, 0, buffer1.Length);
if (read1 == 0)
return stream2.Read(buffer2, 0, 1) == 0; // check not eof
// both stream read could return different counts
int read2 = 0;
do
{
int read3 = stream2.Read(buffer2, read2, read1 - read2);
if (read3 == 0)
return false;
read2 += read3;
}
while (read2 < read1);
if (!IsSame(buffer1, buffer2))
return false;
}
while (true);
}
public static bool IsSame(byte[] bytes1, byte[] bytes2)
{
if (bytes1 == null)
throw new ArgumentNullException(nameof(bytes1));
if (bytes2 == null)
throw new ArgumentNullException(nameof(bytes2));
if (bytes1.Length != bytes2.Length)
return false;
for (int i = 0; i < bytes1.Length; i++)
{
if (bytes1[i] != bytes2[i])
return false;
}
return true;
}
Creo que hay aplicaciones donde "hash" es más rápido que comparar byte por byte. Si necesita comparar un archivo con otros o tener una miniatura de una foto que puede cambiar. Depende de dónde y cómo se usa.
private bool CompareFilesByte(string file1, string file2)
{
using (var fs1 = new FileStream(file1, FileMode.Open))
using (var fs2 = new FileStream(file2, FileMode.Open))
{
if (fs1.Length != fs2.Length) return false;
int b1, b2;
do
{
b1 = fs1.ReadByte();
b2 = fs2.ReadByte();
if (b1 != b2 || b1 < 0) return false;
}
while (b1 >= 0);
}
return true;
}
private string HashFile(string file)
{
using (var fs = new FileStream(file, FileMode.Open))
using (var reader = new BinaryReader(fs))
{
var hash = new SHA512CryptoServiceProvider();
hash.ComputeHash(reader.ReadBytes((int)file.Length));
return Convert.ToBase64String(hash.Hash);
}
}
private bool CompareFilesWithHash(string file1, string file2)
{
var str1 = HashFile(file1);
var str2 = HashFile(file2);
return str1 == str2;
}
Aquí, puedes obtener lo que es más rápido.
var sw = new Stopwatch();
sw.Start();
var compare1 = CompareFilesWithHash(receiveLogPath, logPath);
sw.Stop();
Debug.WriteLine(string.Format("Compare using Hash {0}", sw.ElapsedTicks));
sw.Reset();
sw.Start();
var compare2 = CompareFilesByte(receiveLogPath, logPath);
sw.Stop();
Debug.WriteLine(string.Format("Compare byte-byte {0}", sw.ElapsedTicks));
Opcionalmente, podemos guardar el hash en una base de datos.
Espero que esto pueda ayudar
El método más lento posible es comparar dos archivos byte por byte. Lo más rápido que he podido encontrar es una comparación similar, pero en lugar de un byte a la vez, usaría una matriz de bytes del tamaño de Int64, y luego compararía 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 superaba un escenario de ReadByte () directo en casi 3: 1. Promedió más de 1000 ejecuciones, obtuve este método a 1063 ms, y el siguiente método (byte directo por comparación de bytes) a 3031 ms. Hashing siempre regresó por debajo del segundo a alrededor de un promedio de 865 ms. Esta prueba fue con un archivo de video de ~ 100MB.
Aquí están los métodos de ReadByte y hash que usé, para propósitos 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;
}
Esto que he encontrado funciona bien comparando primero la longitud sin leer datos y luego comparando la secuencia de lectura de bytes
private static bool IsFileIdentical(string a, string b)
{
if (new FileInfo(a).Length != new FileInfo(b).Length) return false;
return (File.ReadAllBytes(a).SequenceEqual(File.ReadAllBytes(b)));
}
Honestamente, creo que debes podar tu árbol de búsqueda tanto como sea posible.
Cosas que verificar antes de ir byte a byte:
- ¿Los tamaños son los mismos?
- Es el último byte en el archivo A diferente del archivo B
Además, la lectura de bloques grandes a la vez será más eficiente ya que las unidades leen los bytes secuenciales más rápidamente. Ir byte a byte no solo causa muchas más llamadas al sistema, sino que hace que el cabezal de lectura de un disco duro tradicional busque más adelante si ambos archivos están en la misma unidad.
Lea el fragmento A y el fragmento B en un búfer de bytes, y compárelos (NO use Array.Equals, vea los comentarios). Ajuste el tamaño de los bloques hasta que llegue a lo que cree que es una buena compensación entre la memoria y el rendimiento. También puede realizar múltiples subprocesos de la comparación, pero no multiplique las lecturas del disco.
Lo único que puede hacer una comparación de suma de comprobación ligeramente más rápida que una comparación byte a byte es el hecho de que está leyendo un archivo a la vez, lo que reduce un poco el tiempo de búsqueda de la cabeza del disco. Sin embargo, esa pequeña ganancia puede ser consumida por el tiempo adicional de calcular el hash.
Además, una comparación de suma de comprobación por supuesto solo tiene posibilidades de ser más rápida si los archivos son idénticos. Si no lo son, una comparación byte a byte terminaría en la primera diferencia, lo que lo haría mucho más rápido.
También debe considerar que una comparación de código hash solo le indica que es muy probable que los archivos sean idénticos. Para estar 100% seguro de que necesita hacer una comparación de byte a byte.
Si el código hash, por ejemplo, es de 32 bits, tiene un 99.99999998% de certeza de que los archivos son idénticos si coinciden los códigos hash. Eso está cerca del 100%, pero si realmente necesita una certeza del 100%, eso no es todo.
Mi respuesta es un derivado de @lars pero corrige el error en la llamada a Stream.Read
. También agregué una revisión de ruta rápida que tenían otras respuestas y validación de entrada. En resumen, esta debería ser la respuesta:
using System;
using System.IO;
namespace ConsoleApp4
{
class Program
{
static void Main(string[] args)
{
var fi1 = new FileInfo(args[0]);
var fi2 = new FileInfo(args[1]);
Console.WriteLine(FilesContentsAreEqual(fi1, fi2));
}
public static bool FilesContentsAreEqual(FileInfo fileInfo1, FileInfo fileInfo2)
{
if (fileInfo1 == null)
{
throw new ArgumentNullException(nameof(fileInfo1));
}
if (fileInfo2 == null)
{
throw new ArgumentNullException(nameof(fileInfo2));
}
if (string.Equals(fileInfo1.FullName, fileInfo2.FullName, StringComparison.OrdinalIgnoreCase))
{
return true;
}
if (fileInfo1.Length != fileInfo2.Length)
{
return false;
}
else
{
using (var file1 = fileInfo1.OpenRead())
{
using (var file2 = fileInfo2.OpenRead())
{
return StreamsContentsAreEqual(file1, file2);
}
}
}
}
private static int ReadFullBuffer(Stream stream, byte[] buffer)
{
int bytesRead = 0;
while (bytesRead < buffer.Length)
{
int read = stream.Read(buffer, bytesRead, buffer.Length - bytesRead);
if (read == 0)
{
// Reached end of stream.
return bytesRead;
}
bytesRead += read;
}
return bytesRead;
}
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 = ReadFullBuffer(stream1, buffer1);
int count2 = ReadFullBuffer(stream2, buffer2);
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;
}
}
}
}
}
}
O si quieres ser súper increíble, puedes usar la variante asíncrona:
using System;
using System.IO;
using System.Threading.Tasks;
namespace ConsoleApp4
{
class Program
{
static void Main(string[] args)
{
var fi1 = new FileInfo(args[0]);
var fi2 = new FileInfo(args[1]);
Console.WriteLine(FilesContentsAreEqualAsync(fi1, fi2).GetAwaiter().GetResult());
}
public static async Task<bool> FilesContentsAreEqualAsync(FileInfo fileInfo1, FileInfo fileInfo2)
{
if (fileInfo1 == null)
{
throw new ArgumentNullException(nameof(fileInfo1));
}
if (fileInfo2 == null)
{
throw new ArgumentNullException(nameof(fileInfo2));
}
if (string.Equals(fileInfo1.FullName, fileInfo2.FullName, StringComparison.OrdinalIgnoreCase))
{
return true;
}
if (fileInfo1.Length != fileInfo2.Length)
{
return false;
}
else
{
using (var file1 = fileInfo1.OpenRead())
{
using (var file2 = fileInfo2.OpenRead())
{
return await StreamsContentsAreEqualAsync(file1, file2).ConfigureAwait(false);
}
}
}
}
private static async Task<int> ReadFullBufferAsync(Stream stream, byte[] buffer)
{
int bytesRead = 0;
while (bytesRead < buffer.Length)
{
int read = await stream.ReadAsync(buffer, bytesRead, buffer.Length - bytesRead).ConfigureAwait(false);
if (read == 0)
{
// Reached end of stream.
return bytesRead;
}
bytesRead += read;
}
return bytesRead;
}
private static async Task<bool> StreamsContentsAreEqualAsync(Stream stream1, Stream stream2)
{
const int bufferSize = 1024 * sizeof(Int64);
var buffer1 = new byte[bufferSize];
var buffer2 = new byte[bufferSize];
while (true)
{
int count1 = await ReadFullBufferAsync(stream1, buffer1).ConfigureAwait(false);
int count2 = await ReadFullBufferAsync(stream2, buffer2).ConfigureAwait(false);
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;
}
}
}
}
}
}
Mis experimentos muestran que definitivamente ayuda a llamar a Stream.ReadByte () menos veces, pero usar BitConverter para empaquetar bytes no hace mucha diferencia en comparación con la comparación de bytes en una matriz de bytes.
Por lo tanto, es posible reemplazar ese ciclo "Math.Ceiling and iterations" en el comentario anterior por el más simple:
for (int i = 0; i < count1; i++)
{
if (buffer1[i] != buffer2[i])
return false;
}
Supongo que tiene que ver con el hecho de que BitConverter.ToInt64 necesita hacer un poco de trabajo (verificar argumentos y luego realizar el cambio de bit) antes de comparar y eso termina siendo la misma cantidad de trabajo que comparar 8 bytes en dos matrices .
Otra mejora en archivos de gran tamaño con una longitud idéntica podría ser no leer los archivos secuencialmente, sino comparar bloques más o menos aleatorios.
Puede usar múltiples hilos, comenzando en diferentes posiciones en el archivo y comparando hacia delante o hacia atrás.
De esta forma puede detectar los cambios en el medio / final del archivo, más rápido de lo que lo haría usando un enfoque secuencial.
Otra respuesta más, derivada de @chsh. MD5 con usos y accesos directos para el mismo archivo, el archivo no existe y las diferentes longitudes:
/// <summary>
/// Performs an md5 on the content of both files and returns true if
/// they match
/// </summary>
/// <param name="file1">first file</param>
/// <param name="file2">second file</param>
/// <returns>true if the contents of the two files is the same, false otherwise</returns>
public static bool IsSameContent(string file1, string file2)
{
if (file1 == file2)
return true;
FileInfo file1Info = new FileInfo(file1);
FileInfo file2Info = new FileInfo(file2);
if (!file1Info.Exists && !file2Info.Exists)
return true;
if (!file1Info.Exists && file2Info.Exists)
return false;
if (file1Info.Exists && !file2Info.Exists)
return false;
if (file1Info.Length != file2Info.Length)
return false;
using (FileStream file1Stream = file1Info.OpenRead())
using (FileStream file2Stream = file2Info.OpenRead())
{
byte[] firstHash = MD5.Create().ComputeHash(file1Stream);
byte[] secondHash = MD5.Create().ComputeHash(file2Stream);
for (int i = 0; i < firstHash.Length; i++)
{
if (i>=secondHash.Length||firstHash[i] != secondHash[i])
return false;
}
return true;
}
}
Se está haciendo aún más rápido si no se lee en pequeños fragmentos de 8 bytes, sino que se da vuelta y se lee un fragmento más grande. Reduje el tiempo promedio de comparación a 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;
}
}
}
}
}
Si decide que realmente necesita una comparación completa byte a byte (ver otras respuestas para la discusión de hash), entonces la solución de una línea es:
bool bFilesAreEqual = File.ReadAllBytes(path1).SequenceEqual(File.ReadAllBytes(path2));
A diferencia de otras respuestas publicadas, esto funciona correctamente para cualquier tipo de archivo: binario, de texto, de medios, ejecutable, etc., pero como una comparación binaria completa , archivos que difieren solo en formas "no importantes" (como BOM , line-ending , codificación de caracteres , metadatos de medios, espacios en blanco, relleno, comentarios de código fuente, etc.) siempre se considerarán no iguales .
Este código carga ambos archivos en la memoria por completo, por lo que no debe usarse para comparar archivos gigantescos . Aparte de esa consideración, la carga completa no es realmente una penalización; de hecho, esta podría ser una solución .NET óptima para tamaños de archivo que se espera que sean inferiores a 85K , ya que las asignaciones pequeñas en .NET
son muy económicas y 85K máximo el rendimiento y la optimización de archivos al CLR
/ BCL
.
Además, para tales escenarios cotidianos, las preocupaciones sobre el rendimiento de la comparación byte por byte a través de los enumeradores LINQ
(como se muestra aquí) son irrelevantes, ya que presionar el disco para E / S de archivos eclipsará, en varios órdenes de magnitud, los beneficios de las diversas alternativas de comparación de memoria. Por ejemplo, aunque SequenceEqual
de hecho nos da la "optimización" de abandonar en el primer desajuste , esto apenas importa después de haber recuperado el contenido de los archivos, cada uno completamente necesario para confirmar el emparejamiento.
Por otro lado, el código anterior no incluye el aborto ansioso para archivos de diferentes tamaños , lo que puede proporcionar una diferencia de rendimiento tangible (posiblemente medible). Este es tangible porque, mientras que la longitud del archivo está disponible en la estructura WIN32_FILE_ATTRIBUTE_DATA
(que debe WIN32_FILE_ATTRIBUTE_DATA
todos modos para cualquier acceso a archivos), continuar con el acceso al contenido del archivo requiere una búsqueda completamente diferente que potencialmente podría evitarse. Si le preocupa esto, la solución se convierte en dos líneas:
// slight optimization over the code shown above
bool bFilesAreEqual =
new FileInfo(path1).Length == new FileInfo(path2).Length &&
File.ReadAllBytes(path1).SequenceEqual(File.ReadAllBytes(path2));
También podría extender esto para evitar las recuperaciones secundarias si se encuentra que los valores de Length
(equivalentes) son cero (no se muestran) y / o para evitar construir cada FileInfo
dos veces (tampoco se muestra).
Si los archivos no son demasiado grandes, puede usar:
public static byte[] ComputeFileHash(string fileName)
{
using (var stream = File.OpenRead(fileName))
return System.Security.Cryptography.MD5.Create().ComputeHash(stream);
}
Solo será factible comparar hashes si los hash son útiles para almacenar.
(Editado el código a algo mucho más limpio.)
Si solo necesitas comparar dos archivos, creo que la forma más rápida sería (en C, no sé si es aplicable a .NET)
- abra ambos archivos f1, f2
- obtener la longitud de archivo respectiva l1, l2
- si l1! = l2 los archivos son diferentes; detener
- mmap () ambos archivos
- use memcmp () en los archivos mmap () ed
OTOH, si necesita encontrar si hay archivos duplicados en un conjunto de N archivos, entonces la forma más rápida es, sin duda, utilizar un hash para evitar comparaciones punto por bit en N-way.
Una comparación de suma de comprobación probablemente sea más lenta que una comparación de byte a byte.
Para generar una suma de comprobación, deberá cargar cada byte del archivo y realizar el procesamiento en él. Luego tendrá que hacer esto en el segundo archivo. El procesamiento será casi definitivamente más lento que el control de comparación.
En cuanto a generar una suma de comprobación: puede hacerlo fácilmente con las clases de criptografía. Aquí hay un breve ejemplo de generación de una suma de comprobación MD5 con C #.
Sin embargo, una suma de comprobación puede ser más rápida y tener más sentido si puede precomputar la suma de comprobación del caso "prueba" o "base". Si tiene un archivo existente, y está comprobando si un archivo nuevo es el mismo que el existente, precomputar la suma de verificación en su archivo "existente" significaría que solo necesita hacer el DiskIO una vez, en el archivo nuevo. Esto probablemente sea más rápido que una comparación de byte a byte.
Editar: ¡ Este método no funcionaría para comparar archivos binarios!
En .NET 4.0, la clase File
tiene los siguientes dos nuevos métodos:
public static IEnumerable<string> ReadLines(string path)
public static IEnumerable<string> ReadLines(string path, Encoding encoding)
Lo que significa que podrías usar:
bool same = File.ReadLines(path1).SequenceEqual(File.ReadLines(path2));