Approcci tipici raccomandano di leggere il binario tramite FileStream e di confrontarlo byte per byte.
- Un confronto di checksum come CRC sarebbe più veloce?
- Esistono librerie .NET in grado di generare un checksum per un file?
Approcci tipici raccomandano di leggere il binario tramite FileStream e di confrontarlo byte per byte.
Risposte:
Molto probabilmente un confronto di checksum sarà più lento di un confronto byte per byte.
Per generare un checksum, devi caricare ogni byte del file ed eseguirne l'elaborazione. Dovrai quindi farlo sul secondo file. L'elaborazione sarà quasi sicuramente più lenta del controllo di confronto.
Per quanto riguarda la generazione di un checksum: puoi farlo facilmente con le classi di crittografia. Ecco un breve esempio di generazione di un checksum MD5 con C #.
Tuttavia, un checksum può essere più rapido e più sensato se è possibile pre-calcolare il checksum del caso "test" o "base". Se hai un file esistente e stai verificando se un nuovo file è uguale a quello esistente, pre-calcolare il checksum sul tuo file "esistente" significherebbe dover fare il DiskIO una sola volta, sul nuovo file. Questo sarebbe probabilmente più veloce di un confronto byte per byte.
Il metodo più lento possibile è confrontare due file byte per byte. Il più veloce che sono stato in grado di inventare è un confronto simile, ma invece di un byte alla volta, useresti un array di byte dimensionato su Int64, quindi confronterai i numeri risultanti.
Ecco cosa mi è venuto in mente:
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;
}
Nei miei test, sono stato in grado di vedere questo sovraperformare uno scenario semplice ReadByte () di quasi 3: 1. Con una media di oltre 1000 esecuzioni, ho ottenuto questo metodo a 1063 ms e il metodo seguente (semplice confronto byte per byte) a 3031 ms. L'hashing è sempre tornato al secondo secondo a circa 865 ms. Questo test è stato eseguito con un file video di ~ 100 MB.
Ecco i metodi di lettura e hashing che ho usato, a scopo di confronto:
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;
}
FilesAreEqual_Hash
metodo dovrebbe avere un using
flusso su entrambi i flussi come il ReadByte
metodo, altrimenti si bloccherà su entrambi i file.
FileStream.Read()
potrebbe effettivamente leggere meno byte rispetto al numero richiesto. Dovresti usare StreamReader.ReadBlock()
invece.
Se tu non decide che si ha realmente bisogno di un confronto pieno di byte per byte (vedi altre risposte per la discussione di hashing), allora la soluzione più semplice è:
• per System.IO.FileInfo
esempio:
public static bool AreFileContentsEqual(FileInfo fi1, FileInfo fi2) =>
fi1.Length == fi2.Length &&
(fi1.Length == 0 || File.ReadAllBytes(fi1.FullName).SequenceEqual(
File.ReadAllBytes(fi2.FullName)));
• per i System.String
nomi dei percorsi:
public static bool AreFileContentsEqual(String path1, String path2) =>
AreFileContentsEqual(new FileInfo(path1), new FileInfo(path2));
A differenza di altre risposte postate, questo è definitivamente corretto per qualsiasi tipo di file: binario, testo, media, eseguibile, ecc., Ma come un confronto binario completo , file che differiscono solo in modi "non importanti" (come BOM , riga -ending , codifica dei caratteri , metadati multimediali, spazi bianchi, riempimento, commenti del codice sorgente, ecc.) saranno sempre considerati non uguali .
Questo codice carica completamente entrambi i file in memoria, quindi non dovrebbe essere usato per confrontare file veramente giganteschi . Al di là di questo importante avvertimento, il caricamento completo non è in realtà una penalità dato il design di .NET GC (perché è fondamentalmente ottimizzato per mantenere allocazioni di piccole dimensioni e di breve durata estremamente economiche ), e in effetti potrebbe anche essere ottimale quando sono attese dimensioni del file essere inferiore a 85 KB , poiché l'utilizzo di un minimo di codice utente (come mostrato qui) implica la delega massima dei problemi di prestazioni dei file alCLR
, BCL
eJIT
di beneficiare di (ad esempio) l'ultima tecnologia di progettazione, codice di sistema, e le ottimizzazioni runtime adattivi.
Inoltre, per tali scenari di lavoro quotidiano, le preoccupazioni sulle prestazioni del confronto byte per byte tramite LINQ
enumeratori (come mostrato qui) sono controverse, poiché colpire il disco a̲t̲ a̲l̲l̲ per l'I / O dei file diminuirà, di diversi ordini di grandezza, i vantaggi delle varie alternative di confronto della memoria. Ad esempio, anche se SequenceEqual
non di fatto darci la "ottimizzazione" di abbandonare il primo mancata corrispondenza , questo poco importa dopo aver già recuperato il contenuto dei file, ognuna completamente necessario confermare la partita ..
Oltre alla risposta di Reed Copsey :
Il caso peggiore è dove i due file sono identici. In questo caso è meglio confrontare i file byte per byte.
Se i due file non sono identici, puoi velocizzare un po 'le cose rilevando prima che non sono identici.
Ad esempio, se i due file hanno una lunghezza diversa, sai che non possono essere identici e non devi nemmeno confrontare il loro contenuto reale.
Sta diventando ancora più veloce se non leggi in piccoli blocchi da 8 byte ma fai un giro in giro, leggendo un pezzo più grande. Ho ridotto il tempo medio di confronto 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;
}
}
}
}
}
count1 != count2
non è corretto. Stream.Read()
può restituire un importo inferiore al conteggio fornito, per vari motivi.
Int64
blocchi, si consiglia di calcolare la dimensione in questo modo: const int bufferSize = 1024 * sizeof(Int64)
.
L'unica cosa che potrebbe rendere un confronto di checksum leggermente più veloce di un confronto byte per byte è il fatto che stai leggendo un file alla volta, riducendo in qualche modo il tempo di ricerca per la testina del disco. Questo leggero guadagno può tuttavia essere ben consumato dal tempo aggiuntivo di calcolo dell'hash.
Inoltre, un confronto di checksum ovviamente ha solo la possibilità di essere più veloce se i file sono identici. In caso contrario, un confronto byte per byte terminerebbe alla prima differenza, rendendolo molto più veloce.
Dovresti anche considerare che un confronto del codice hash ti dice solo che è molto probabile che i file siano identici. Per essere certi al 100% devi fare un confronto byte per byte.
Se il codice hash, ad esempio, è di 32 bit, sei sicuro al 99.99999998% che i file sono identici se i codici hash corrispondono. Questo è vicino al 100%, ma se hai davvero bisogno del 100% di certezza, non è così.
1 - (1 / (2^32))
, che è la probabilità che ogni singolo file abbia un dato hash a 32 bit. La probabilità che due file diversi abbiano lo stesso hash è la stessa, perché il primo file fornisce il valore hash "dato" e dobbiamo solo considerare se l'altro file corrisponde a quel valore. Le probabilità con hashing a 64 e 128 bit diminuiscono a 99.999999999999999994% e 99.99999999999999999999999999999999999999997% (rispettivamente), come se ciò contasse con tali numeri insondabili.
Modifica: questo metodo no funzionerebbe per il confronto di file binari!
In .NET 4.0, la File
classe ha i seguenti due nuovi metodi:
public static IEnumerable<string> ReadLines(string path)
public static IEnumerable<string> ReadLines(string path, Encoding encoding)
Ciò significa che potresti usare:
bool same = File.ReadLines(path1).SequenceEqual(File.ReadLines(path2));
Onestamente, penso che tu debba potare il più possibile il tuo albero di ricerca.
Cose da controllare prima di passare byte per byte:
Inoltre, leggere blocchi di grandi dimensioni alla volta sarà più efficiente poiché le unità leggono i byte sequenziali più rapidamente. L'andare byte per byte provoca non solo molte più chiamate di sistema, ma fa sì che la testina di lettura di un disco rigido tradizionale cerchi avanti e indietro più spesso se entrambi i file si trovano sulla stessa unità.
Leggi il blocco A e il blocco B in un buffer di byte e confrontali (NON utilizzare Array.Equals, vedi commenti). Ottimizza la dimensione dei blocchi fino a quando non colpisci quello che ritieni sia un buon compromesso tra memoria e prestazioni. Puoi anche eseguire il multi-thread del confronto, ma non multi-thread le letture del disco.
La mia risposta è una derivata di @lars ma risolve il bug nella chiamata a Stream.Read
. Aggiungo anche un po 'di percorso rapido verificando che avevano altre risposte e immetto la convalida. In breve, questa dovrebbe essere la risposta:
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 se vuoi essere super-fantastico, puoi usare la variante asincrona:
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;
}
}
}
}
}
}
I miei esperimenti dimostrano che aiuta sicuramente a chiamare Stream.ReadByte () meno volte, ma l'uso di BitConverter per pacchetto di byte non fa molta differenza rispetto al confronto di byte in un array di byte.
Quindi è possibile sostituire quel ciclo "Math.Ceiling and iterations" nel commento sopra con il più semplice:
for (int i = 0; i < count1; i++)
{
if (buffer1[i] != buffer2[i])
return false;
}
Immagino che abbia a che fare con il fatto che BitConverter.ToInt64 ha bisogno di fare un po 'di lavoro (controlla gli argomenti e quindi esegui lo spostamento dei bit) prima di confrontare e che finisce per essere la stessa quantità di lavoro di confrontare 8 byte in due array .
Se i file non sono troppo grandi, puoi usare:
public static byte[] ComputeFileHash(string fileName)
{
using (var stream = File.OpenRead(fileName))
return System.Security.Cryptography.MD5.Create().ComputeHash(stream);
}
Sarà possibile confrontare gli hash solo se gli hash sono utili da memorizzare.
(Modificato il codice su qualcosa di molto più pulito.)
Un altro miglioramento su file di grandi dimensioni con identica lunghezza potrebbe essere quello di non leggere i file in sequenza, ma piuttosto di confrontare blocchi più o meno casuali.
È possibile utilizzare più thread, iniziando da diverse posizioni nel file e confrontando avanti o indietro.
In questo modo è possibile rilevare le modifiche a metà / fine del file, più velocemente di quanto ci si aspetterebbe utilizzando un approccio sequenziale.
Se hai solo bisogno di confrontare due file, immagino che il modo più veloce sarebbe (in C, non so se sia applicabile a .NET)
OTOH, se hai bisogno di scoprire se ci sono file duplicati in un set di N file, il modo più veloce è senza dubbio l'uso di un hash per evitare confronti N-way bit per bit.
Qualcosa (si spera) ragionevolmente efficiente:
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;
}
}
Ecco alcune funzioni di utilità che consentono di determinare se due file (o due flussi) contengono dati identici.
Ho fornito una versione "veloce" che è multi-thread in quanto confronta array di byte (ogni buffer riempito da ciò che è stato letto in ciascun file) in thread diversi usando Attività.
Come previsto, è molto più veloce (circa 3 volte più veloce) ma consuma più CPU (perché è multi-thread) e più memoria (perché ha bisogno di buffer di array a due byte per thread di confronto).
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;
}
Penso che ci siano applicazioni in cui "l'hash" è più veloce del confronto byte per byte. Se devi confrontare un file con altri o avere un'anteprima di una foto che può cambiare. Dipende da dove e come sta usando.
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;
}
Qui puoi ottenere qual è il più veloce.
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));
Facoltativamente, possiamo salvare l'hash in un database.
Spero che questo possa aiutare
Ancora un'altra risposta, derivata da @chsh. MD5 con utilizzi e collegamenti per lo stesso file, il file non esiste e le diverse lunghezze:
/// <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;
}
}
if (i>=secondHash.Length ...
In quali circostanze sarebbe due MD5 hash essere diverse lunghezze?
Ho scoperto che funziona bene confrontando prima la lunghezza senza leggere i dati e quindi confrontando la sequenza di byte letti
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)));
}