Pendekatan tipikal merekomendasikan membaca biner melalui FileStream dan membandingkannya byte-by-byte.
- Apakah perbandingan checksum seperti CRC lebih cepat?
- Apakah ada pustaka .NET yang dapat menghasilkan checksum untuk file?
Pendekatan tipikal merekomendasikan membaca biner melalui FileStream dan membandingkannya byte-by-byte.
Jawaban:
Perbandingan checksum kemungkinan besar akan lebih lambat daripada perbandingan byte-by-byte.
Untuk menghasilkan checksum, Anda harus memuat setiap byte file, dan melakukan pemrosesan di atasnya. Anda kemudian harus melakukan ini pada file kedua. Prosesnya hampir pasti akan lebih lambat daripada pemeriksaan perbandingan.
Adapun untuk menghasilkan checksum: Anda dapat melakukan ini dengan mudah dengan kelas kriptografi. Berikut adalah contoh singkat untuk menghasilkan checksum MD5 dengan C #.
Namun, checksum mungkin lebih cepat dan lebih masuk akal jika Anda dapat menghitung sebelumnya checksum untuk kasus "test" atau "base". Jika Anda memiliki file yang sudah ada, dan Anda memeriksa untuk melihat apakah file baru sama dengan yang sudah ada, pra-komputasi checksum pada file "yang ada" berarti hanya perlu melakukan DiskIO satu kali, di file baru. Ini kemungkinan akan lebih cepat daripada perbandingan byte-by-byte.
Metode paling lambat yang mungkin adalah membandingkan dua file byte demi byte. Yang tercepat yang bisa saya dapatkan adalah perbandingan yang serupa, tetapi alih-alih satu byte pada satu waktu, Anda akan menggunakan array byte berukuran Int64, dan kemudian membandingkan angka yang dihasilkan.
Inilah yang saya dapatkan:
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;
}
Dalam pengujian saya, saya dapat melihat ini mengungguli skenario ReadByte () langsung dengan hampir 3: 1. Rata-rata lebih dari 1000 berjalan, saya mendapatkan metode ini pada 1063ms, dan metode di bawah ini (perbandingan byte langsung dengan byte) pada 3031ms. Hashing selalu kembali sub-detik sekitar rata-rata 865ms. Pengujian ini dilakukan dengan file video ~ 100MB.
Inilah ReadByte dan metode hashing yang saya gunakan, untuk tujuan perbandingan:
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
Metode harus memiliki using
pada kedua berkas sungai juga seperti ReadByte
metode selain itu akan bertahan pada kedua file.
FileStream.Read()
sebenarnya mungkin membaca byte kurang dari jumlah yang diminta. Anda harus menggunakan StreamReader.ReadBlock()
sebagai gantinya.
Jika Anda tidak memutuskan Anda benar-benar membutuhkan perbandingan penuh byte-by-byte (lihat jawaban lainnya untuk diskusi dari hashing), maka solusi termudah adalah:
public static bool AreFileContentsEqual(String path1, String path2) =>
File.ReadAllBytes(path1).SequenceEqual(File.ReadAllBytes(path2));
public static bool AreFileContentsEqual(FileInfo fi1, FileInfo fi2) =>
fi1.Length == fi2.Length &&
(fi1.Length == 0 || File.ReadAllBytes(fi1.FullName).SequenceEqual(
File.ReadAllBytes(fi2.FullName)));
Tidak seperti beberapa jawaban yang diposkan lainnya, ini secara meyakinkan benar untuk semua jenis file: biner, teks, media, dapat dieksekusi, dll., Tetapi sebagai perbandingan biner penuh , file yang berbeda hanya dengan cara yang "tidak penting" (seperti BOM , baris -ending , pengkodean karakter , metadata media, spasi, padding, komentar kode sumber, dll.) akan selalu dianggap tidak sama .
Kode ini memuat kedua file ke dalam memori seluruhnya, jadi tidak boleh digunakan untuk membandingkan file yang sangat besar . Di luar peringatan penting itu, pemuatan penuh sebenarnya bukan merupakan penalti mengingat desain .NET GC (karena secara fundamental dioptimalkan untuk menjaga alokasi kecil dan berumur pendek tetap sangat murah ), dan bahkan dapat menjadi optimal ketika ukuran file diharapkan kurang dari 85k , karena menggunakan minimal kode pengguna (seperti yang ditunjukkan di sini) menyiratkan maksimal mendelegasikan masalah kinerja file ke CLR
, BCL
dan JIT
untuk manfaat dari (misalnya) teknologi terbaru desain, kode sistem, dan optimasi runtime adaptif.
Selain itu, untuk skenario hari kerja seperti itu, kekhawatiran tentang kinerja perbandingan byte-by-byte melalui LINQ
enumerator (seperti yang ditunjukkan di sini) diperdebatkan, karena menekan disk a̲t̲ a̲l̲l̲ untuk file I / O akan mengecil, dengan beberapa kali lipat, manfaatnya dari berbagai alternatif pembanding memori. Sebagai contoh, meskipun SequenceEqual
tidak sebenarnya memberi kita "optimasi" dari meninggalkan pada ketidakcocokan pertama , ini hampir tidak penting setelah sudah diambil isinya file, masing-masing sepenuhnya diperlukan untuk mengkonfirmasi pertandingan ..
Selain jawaban Reed Copsey :
Kasus terburuk adalah di mana kedua file itu identik. Dalam hal ini yang terbaik adalah membandingkan file byte-by-byte.
Jika jika kedua file tidak identik, Anda dapat mempercepat sedikit dengan mendeteksi lebih cepat bahwa keduanya tidak identik.
Misalnya, jika kedua file memiliki panjang yang berbeda maka Anda tahu bahwa keduanya tidak dapat identik, dan Anda bahkan tidak perlu membandingkan konten sebenarnya.
Ini menjadi lebih cepat jika Anda tidak membaca dalam potongan kecil 8 byte tetapi membuat putaran, membaca potongan yang lebih besar. Saya mengurangi waktu perbandingan rata-rata menjadi 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
ini tidak benar. Stream.Read()
dapat mengembalikan kurang dari hitungan yang Anda berikan, karena berbagai alasan.
Int64
blok, Anda mungkin ingin menghitung ukuran seperti ini: const int bufferSize = 1024 * sizeof(Int64)
.
Satu-satunya hal yang mungkin membuat perbandingan checksum sedikit lebih cepat daripada perbandingan byte-by-byte adalah kenyataan bahwa Anda membaca satu file pada satu waktu, agak mengurangi waktu pencarian untuk kepala disk. Keuntungan kecil itu bagaimanapun juga bisa dimakan oleh waktu tambahan untuk menghitung hash.
Selain itu, perbandingan checksum tentu saja hanya memiliki peluang lebih cepat jika filenya identik. Jika tidak, perbandingan byte-by-byte akan berakhir pada perbedaan pertama, membuatnya jauh lebih cepat.
Anda juga harus mempertimbangkan bahwa perbandingan kode hash hanya memberi tahu Anda bahwa kemungkinan besar file tersebut identik. Agar 100% yakin, Anda perlu melakukan perbandingan byte-by-byte.
Jika kode hash misalnya adalah 32 bit, Anda sekitar 99.99999998% yakin bahwa file tersebut identik jika kode hash cocok. Itu mendekati 100%, tetapi jika Anda benar-benar membutuhkan kepastian 100%, bukan itu.
1 - (1 / (2^32))
, yang merupakan probabilitas bahwa setiap file akan memiliki beberapa hash 32-bit. Probabilitas dua file berbeda memiliki hash yang sama adalah sama, karena file pertama memberikan nilai hash yang "diberikan", dan kita hanya perlu mempertimbangkan apakah file lain cocok dengan nilai itu atau tidak. Peluang dengan hashing 64- dan 128-bit menurun menjadi 99.999999999999999994% dan 99.99999999999999999999999999999999997% (masing-masing), seolah-olah itu penting dengan angka yang tidak terduga.
Sunting: Metode ini tidak akan berfungsi untuk membandingkan file biner!
Di .NET 4.0, File
kelas memiliki dua metode baru berikut ini:
public static IEnumerable<string> ReadLines(string path)
public static IEnumerable<string> ReadLines(string path, Encoding encoding)
Yang berarti Anda bisa menggunakan:
bool same = File.ReadLines(path1).SequenceEqual(File.ReadLines(path2));
Sejujurnya, saya pikir Anda perlu memangkas pohon pencarian Anda sebanyak mungkin.
Hal-hal yang perlu diperiksa sebelum beralih byte-by-byte:
Selain itu, membaca blok besar pada satu waktu akan lebih efisien karena drive membaca byte berurutan lebih cepat. Menggunakan byte-by-byte tidak hanya menyebabkan lebih banyak panggilan sistem, tetapi juga menyebabkan kepala baca hard drive tradisional mencari bolak-balik lebih sering jika kedua file berada di drive yang sama.
Baca chunk A dan chunk B menjadi buffer byte, dan bandingkan keduanya (JANGAN gunakan Array.Equals, lihat komentar). Sesuaikan ukuran blok sampai Anda mencapai apa yang menurut Anda merupakan pertukaran yang baik antara memori dan kinerja. Anda juga dapat melakukan multi-utas perbandingan, tetapi jangan membuat multi-utas disk yang dibaca.
Jawaban saya adalah turunan dari @lars tetapi memperbaiki bug dalam panggilan ke Stream.Read
. Saya juga menambahkan beberapa pemeriksaan jalur cepat yang dimiliki jawaban lain, dan validasi input. Singkatnya, ini harus yang jawabannya:
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;
}
}
}
}
}
}
Atau jika Anda ingin menjadi super-hebat, Anda dapat menggunakan varian async:
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;
}
}
}
}
}
}
Eksperimen saya menunjukkan bahwa itu pasti membantu untuk memanggil Stream.ReadByte () lebih sedikit, tetapi menggunakan BitConverter untuk memaketkan byte tidak membuat banyak perbedaan dalam membandingkan byte dalam array byte.
Jadi dimungkinkan untuk mengganti loop "Math.Ceiling and iterations" dalam komentar di atas dengan yang paling sederhana:
for (int i = 0; i < count1; i++)
{
if (buffer1[i] != buffer2[i])
return false;
}
Saya kira itu ada hubungannya dengan fakta bahwa BitConverter.ToInt64 perlu melakukan sedikit pekerjaan (periksa argumen dan kemudian lakukan pergeseran bit) sebelum Anda membandingkan dan itu akhirnya menjadi jumlah pekerjaan yang sama dengan membandingkan 8 byte dalam dua array .
Jika file tidak terlalu besar, Anda dapat menggunakan:
public static byte[] ComputeFileHash(string fileName)
{
using (var stream = File.OpenRead(fileName))
return System.Security.Cryptography.MD5.Create().ComputeHash(stream);
}
Membandingkan hash hanya dapat dilakukan jika hash berguna untuk disimpan.
(Mengedit kode menjadi sesuatu yang jauh lebih bersih.)
Perbaikan lain pada file besar dengan panjang yang identik, mungkin dengan tidak membaca file secara berurutan, tetapi membandingkan lebih banyak atau lebih sedikit blok acak.
Anda dapat menggunakan beberapa utas, mulai dari posisi berbeda dalam file dan membandingkan maju atau mundur.
Dengan cara ini Anda dapat mendeteksi perubahan di tengah / akhir file, lebih cepat daripada yang Anda lakukan di sana menggunakan pendekatan sekuensial.
Jika Anda hanya perlu membandingkan dua file, saya kira cara tercepat adalah (di C, saya tidak tahu apakah itu berlaku untuk .NET)
OTOH, jika Anda perlu menemukan apakah ada file duplikat dalam satu set N file, maka cara tercepat tidak diragukan lagi adalah menggunakan hash untuk menghindari perbandingan bit-by-bit N-way.
Sesuatu (semoga) cukup efisien:
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;
}
}
Berikut adalah beberapa fungsi utilitas yang memungkinkan Anda menentukan apakah dua file (atau dua aliran) berisi data yang identik.
Saya telah menyediakan versi "cepat" yang multi-threaded karena membandingkan array byte (setiap buffer diisi dari apa yang telah dibaca di setiap file) di thread berbeda menggunakan Tasks.
Seperti yang diharapkan, ini jauh lebih cepat (sekitar 3x lebih cepat) tetapi mengkonsumsi lebih banyak CPU (karena multi threaded) dan lebih banyak memori (karena membutuhkan dua buffer array byte per thread perbandingan).
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;
}
Saya rasa ada aplikasi di mana "hash" lebih cepat daripada membandingkan byte demi byte. Jika Anda perlu membandingkan file dengan orang lain atau memiliki thumbnail foto yang bisa berubah. Itu tergantung di mana dan bagaimana itu digunakan.
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;
}
Di sini, Anda bisa mendapatkan yang tercepat.
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));
Secara opsional, kita dapat menyimpan hash dalam database.
Semoga ini bisa membantu
Ini saya telah menemukan bekerja dengan baik membandingkan pertama panjangnya tanpa membaca data dan kemudian membandingkan urutan byte yang dibaca
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)));
}
Jawaban lain, berasal dari @chsh. MD5 dengan penggunaan dan shortcut untuk file yang sama, file tidak ada dan panjangnya berbeda:
/// <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 ...
Dalam keadaan apa dua hash MD5 memiliki panjang yang berbeda?