As abordagens típicas recomendam a leitura do binário via FileStream e a comparação, byte a byte.
- Uma comparação de soma de verificação como a CRC seria mais rápida?
- Existem bibliotecas .NET que podem gerar uma soma de verificação para um arquivo?
As abordagens típicas recomendam a leitura do binário via FileStream e a comparação, byte a byte.
Respostas:
Uma comparação de soma de verificação provavelmente será mais lenta que uma comparação de byte a byte.
Para gerar uma soma de verificação, você precisará carregar cada byte do arquivo e executar o processamento nele. Você precisará fazer isso no segundo arquivo. Definitivamente, o processamento será mais lento que a verificação de comparação.
Quanto à geração de uma soma de verificação: você pode fazer isso facilmente com as classes de criptografia. Aqui está um pequeno exemplo de como gerar uma soma de verificação MD5 com C #.
No entanto, uma soma de verificação pode ser mais rápida e fazer mais sentido se você puder pré-calcular a soma de verificação do caso "teste" ou "base". Se você possui um arquivo existente e está verificando se um novo arquivo é igual ao existente, pré-calcular a soma de verificação no arquivo "existente" significaria apenas a necessidade de fazer o DiskIO uma vez, no diretório novo arquivo. Isso provavelmente seria mais rápido que uma comparação de byte a byte.
O método mais lento possível é comparar dois arquivos byte a byte. O mais rápido que consegui fazer é uma comparação semelhante, mas, em vez de um byte por vez, você usaria uma matriz de bytes dimensionada para Int64 e, em seguida, compararia os números resultantes.
Aqui está o que eu vim com:
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;
}
Nos meus testes, eu pude ver isso superar um cenário simples de ReadByte () em quase 3: 1. Em média, mais de 1000 execuções, obtive esse método em 1063ms e o método abaixo (comparação direta de bytes por byte) em 3031ms. O hash sempre voltava em um segundo em torno de uma média de 865ms. Este teste foi realizado com um arquivo de vídeo de ~ 100 MB.
Aqui estão os métodos ReadByte e hash que eu usei, para fins de comparação:
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
método deve ter um using
nos dois fluxos de arquivos também como o ReadByte
método, caso contrário, ele permanecerá nos dois arquivos.
FileStream.Read()
na verdade, pode ler menos bytes que o número solicitado. Você deve usar em seu StreamReader.ReadBlock()
lugar.
Se você não decidir você realmente precisa de uma comparação completa byte a byte (veja outras respostas para a discussão de hashing), então a solução mais fácil é:
• para System.IO.FileInfo
instâncias:
public static bool AreFileContentsEqual(FileInfo fi1, FileInfo fi2) =>
fi1.Length == fi2.Length &&
(fi1.Length == 0 || File.ReadAllBytes(fi1.FullName).SequenceEqual(
File.ReadAllBytes(fi2.FullName)));
• para System.String
nomes de caminhos:
public static bool AreFileContentsEqual(String path1, String path2) =>
AreFileContentsEqual(new FileInfo(path1), new FileInfo(path2));
Diferentemente de outras respostas publicadas, isso é conclusivamente correto para qualquer tipo de arquivo: binário, texto, mídia, executável etc., mas como uma comparação binária completa , arquivos que diferem apenas de maneiras "sem importância" (como BOM , linha -ending , codificação de caracteres , metadados de mídia, espaço em branco, estofamento, comentários de código-fonte, etc.) será sempre considerado não-igual .
Esse código carrega os dois arquivos inteiramente na memória, portanto, não deve ser usado para comparar arquivos verdadeiramente gigantescos . Além dessa ressalva importante, o carregamento completo não é realmente uma penalidade, devido ao design do .NET GC (porque é fundamentalmente otimizado para manter alocações pequenas e de curta duração extremamente baratas ) e, de fato, pode ser ideal quando os tamanhos de arquivo são esperados a ser menos do que 85K , porque usar um mínimo de código de utilizador (como mostrado aqui) implica maximamente delegar problemas de desempenho arquivo para o CLR
, BCL
e JIT
para benefício de (por exemplo) o mais recente projeto, código do sistema, e otimizações de tempo de execução adaptativos.
Além disso, para esses cenários de trabalho, preocupações com o desempenho da comparação de byte a byte através de LINQ
enumeradores (como mostrado aqui) são discutíveis, já que bater no disco a̲t̲ a̲l̲l̲ para E / S de arquivo diminui, por várias ordens de magnitude, os benefícios das várias alternativas de comparação de memória. Por exemplo, embora SequenceEqual
realmente nos dê a "otimização" de abandonar na primeira incompatibilidade , isso dificilmente importa depois de já ter buscado o conteúdo dos arquivos, cada um totalmente necessário para confirmar a correspondência.
Além da resposta de Reed Copsey :
O pior caso é onde os dois arquivos são idênticos. Nesse caso, é melhor comparar os arquivos byte a byte.
Se os dois arquivos não forem idênticos, você poderá acelerar um pouco as coisas, detectando antes que eles não são idênticos.
Por exemplo, se os dois arquivos tiverem tamanho diferente, você saberá que eles não podem ser idênticos e nem precisará comparar o conteúdo real.
Está ficando ainda mais rápido se você não ler em pequenos pedaços de 8 bytes, mas fizer um loop, lendo um pedaço maior. Reduzi o tempo médio de comparação para 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
não está correta. Stream.Read()
pode retornar menos do que a contagem que você forneceu, por vários motivos.
Int64
blocos, você pode querer calcular o tamanho da seguinte forma: const int bufferSize = 1024 * sizeof(Int64)
.
A única coisa que pode fazer uma comparação de soma de verificação um pouco mais rápida que uma comparação de byte a byte é o fato de você estar lendo um arquivo de cada vez, reduzindo um pouco o tempo de busca da cabeça do disco. Esse pequeno ganho pode, no entanto, muito bem ser consumido pelo tempo adicional de cálculo do hash.
Além disso, é claro que uma comparação de soma de verificação só tem chance de ser mais rápida se os arquivos forem idênticos. Caso contrário, uma comparação de byte a byte terminaria na primeira diferença, tornando-a muito mais rápida.
Você também deve considerar que uma comparação de código de hash apenas informa que é muito provável que os arquivos sejam idênticos. Para ter 100% de certeza, você precisa fazer uma comparação byte a byte.
Se o código de hash, por exemplo, for de 32 bits, você terá 99,99999998% de certeza de que os arquivos serão idênticos se os códigos de hash corresponderem. Isso é quase 100%, mas se você realmente precisa de 100% de certeza, não é isso.
1 - (1 / (2^32))
, que é a probabilidade de que um único arquivo tenha um hash de 32 bits. A probabilidade de dois arquivos diferentes terem o mesmo hash é a mesma, porque o primeiro arquivo fornece o valor de hash "fornecido" e precisamos considerar apenas se o outro arquivo corresponde ou não a esse valor. As chances de hash de 64 e 128 bits diminuem para 99,999999999999999994% e 99,99999999999999999999999999999999997% (respectivamente), como se isso importasse com esses números insondáveis.
Edit: Este método não funcionaria para comparar arquivos binários!
No .NET 4.0, a File
classe possui os dois novos métodos a seguir:
public static IEnumerable<string> ReadLines(string path)
public static IEnumerable<string> ReadLines(string path, Encoding encoding)
O que significa que você pode usar:
bool same = File.ReadLines(path1).SequenceEqual(File.ReadLines(path2));
Honestamente, acho que você precisa reduzir sua árvore de pesquisa o máximo possível.
Itens a serem verificados antes de seguir byte a byte:
Além disso, a leitura de grandes blocos de cada vez será mais eficiente, pois as unidades leem bytes seqüenciais mais rapidamente. Passar byte a byte causa não apenas muito mais chamadas do sistema, como também faz com que o cabeçote de leitura de um disco rígido tradicional procure e volte com mais frequência se os dois arquivos estiverem na mesma unidade.
Leia o fragmento A e o fragmento B em um buffer de bytes e compare-os (NÃO use Array.Equals, consulte comentários). Ajuste o tamanho dos blocos até atingir o que você acha que é uma boa troca entre memória e desempenho. Você também pode multi-thread a comparação, mas não multi-thread o disco lê.
Minha resposta é derivada de @lars, mas corrige o erro na chamada para Stream.Read
. Também adiciono algumas verificações rápidas que outras respostas tiveram e validação de entrada. Em suma, esta deve ser a resposta:
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;
}
}
}
}
}
}
Ou, se você quiser ser super incrível, use a variante assí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;
}
}
}
}
}
}
Minhas experiências mostram que definitivamente ajuda a chamar Stream.ReadByte () menos vezes, mas usar o BitConverter para empacotar bytes não faz muita diferença em comparação à comparação de bytes em uma matriz de bytes.
Portanto, é possível substituir o loop "Math.Ceiling e iterações" no comentário acima pelo mais simples:
for (int i = 0; i < count1; i++)
{
if (buffer1[i] != buffer2[i])
return false;
}
Eu acho que isso tem a ver com o fato de que o BitConverter.ToInt64 precisa fazer um pouco de trabalho (verificar argumentos e depois executar a troca de bits) antes de comparar e isso acaba sendo a mesma quantidade de trabalho que comparar 8 bytes em duas matrizes .
Se os arquivos não forem muito grandes, você pode usar:
public static byte[] ComputeFileHash(string fileName)
{
using (var stream = File.OpenRead(fileName))
return System.Security.Cryptography.MD5.Create().ComputeHash(stream);
}
Só será possível comparar hashes se eles forem úteis para armazenar.
(Editou o código para algo muito mais limpo.)
Outra melhoria em arquivos grandes com comprimento idêntico pode ser não ler os arquivos sequencialmente, mas comparar blocos mais ou menos aleatórios.
Você pode usar vários threads, iniciando em diferentes posições no arquivo e comparando para frente ou para trás.
Dessa forma, você pode detectar alterações no meio / final do arquivo, mais rapidamente do que você chegaria lá usando uma abordagem seqüencial.
Se você precisar comparar apenas dois arquivos, acho que o caminho mais rápido seria (em C, não sei se é aplicável ao .NET)
OTOH, se você precisar descobrir se há arquivos duplicados em um conjunto de N arquivos, o caminho mais rápido será, sem dúvida, usar um hash para evitar comparações N-bit bit a bit.
Algo (espero) razoavelmente 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;
}
}
Aqui estão algumas funções utilitárias que permitem determinar se dois arquivos (ou dois fluxos) contêm dados idênticos.
Forneci uma versão "rápida" com vários segmentos, pois compara matrizes de bytes (cada buffer preenchido com o que foi lido em cada arquivo) em diferentes segmentos usando Tarefas.
Como esperado, é muito mais rápido (cerca de 3x mais rápido), mas consome mais CPU (por ser multiencadeado) e mais memória (porque precisa de dois buffers de matriz de bytes por thread de comparação).
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;
}
Eu acho que existem aplicativos em que "hash" é mais rápido do que comparar byte por byte. Se você precisar comparar um arquivo com outras pessoas ou ter uma miniatura de uma foto que possa ser alterada. Depende de onde e como está sendo usado.
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;
}
Aqui, você pode obter o que é mais 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 salvar o hash em um banco de dados.
Espero que isso possa ajudar
Outra resposta, derivada de @chsh. MD5 com utilizações e atalhos para o mesmo arquivo, arquivo não existe e comprimentos diferentes:
/// <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 ...
sob que circunstâncias dois hashes MD5 teriam comprimentos diferentes?
Descobri que funciona bem comparando primeiro o comprimento sem ler os dados e, em seguida, comparando a sequência de bytes lidos
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)));
}