C# ile Aynı Dosyaları Bulma

Aynı dosyalar farklı adlar ve klasörler altında saklandığında disk alanı gereksiz yere dolar. Bu rehber, C# ile SHA256 dosya hash değerini üretmeyi, dosya içeriklerini karşılaştırmayı ve bir klasördeki tekrarları gruplamayı anlatır. Akış tabanlı örnekler büyük dosyalarda bellek kullanımını sınırlar. Boyuta göre ön gruplama yaklaşımı ise gereksiz hash işlemlerini azaltır.

32 kez okundu 13 dk 6 sn okuma süresi 26 Temmuz 2026
Google News Google News ile Abone Ol 0 Yorum

C# ile Aynı Dosyaları Bulma Neden Önemli?

Kullanıcılar aynı görseli, videoyu, belgeyi veya arşiv dosyasını zaman içinde farklı klasörlere kopyalayabilir. Örneğin bir tasarım ekibi aynı PNG dosyasını “final”, “son” ve “kopya” adlarıyla saklayabilir. Dosya adları değişse bile içerik aynı kalır. Öncelikle yalnızca dosya adına bakan bir uygulama bu tekrarları kaçırır. Dosya boyutu daha güçlü bir ipucu verir; ancak aynı boyut, aynı içeriği garanti etmez. Bu nedenle güvenilir bir tarayıcı dosya içeriğinden kriptografik bir özet üretir. SHA-256 her dosya için 256 bitlik bir değer oluşturur. Ardından uygulama aynı boyut ve aynı hash değerini taşıyan dosyaları tek grupta toplar. Böylece kullanıcı, kopyaları silmeden önce açık bir inceleme listesi görür.

Dosya Hash Değeri Nedir?

Hash algoritması, dosyanın tüm baytlarını sabit uzunlukta bir özete dönüştürür. Örneğin bir dosyadaki tek bayt bile değiştiğinde SHA-256 değeri büyük ölçüde değişir. Bu özellik içerik karşılaştırmasını dosya adından bağımsız hâle getirir. Bunun yanında aynı dosya her çalıştırmada aynı SHA-256 değerini üretir. Uygulama bu değeri genellikle 64 karakterlik onaltılık metin biçiminde gösterir. Çünkü SHA-256 çıktısı 32 bayt, yani 256 bit uzunluğundadır.

Önemli ayrım:Hash değeri dosyayı şifrelemez ve içerikten geri dönüş sağlamaz. Kod bu değeri yalnızca karşılaştırma ve bütünlük denetimi için kullanır.

Dosya Adı, Boyut ve Hash Karşılaştırması

Her kontrol yöntemi farklı bir hız ve doğruluk düzeyi sunar. Bu nedenle uygulama önce ucuz kontrolleri çalıştırmalı, SHA-256 hesabını yalnızca aday dosyalara uygulamalıdır.

YöntemHızDoğrulukUygun kullanım
Dosya adıÇok yüksekDüşükBenzer adları filtreleme
Dosya boyutuÇok yüksekOrtaHash adaylarını daraltma
SHA-256Dosya okumaya bağlıÇok yüksekAynı içeriği doğrulama
Bayt bayt kontrolDosya okumaya bağlıKesinKritik son doğrulama
Boyut ön elemesinin hız kazandırdığı SHA-256 dosya karşılaştırma akışı

Boyut ön elemesinin hız kazandırdığı SHA-256 dosya karşılaştırma akışı

Proje Kurulumu

Öncelikle güncel .NET SDK ile bir konsol projesi oluşturun. System.Security.Cryptography ad alanı .NET çalışma zamanı içinde gelir. Bu yüzden örnek için ek bir NuGet paketi gerekmez.

dotnet new console -n AyniDosyaBulucu cd AyniDosyaBulucu dotnet run

Örnekler modern SHA256.HashData API’sini kullanır. Ayrıca Convert.ToHexString metodu bayt dizisini okunabilir bir hash metnine çevirir. Eski .NET Framework projeleri için rehberin ilerleyen bölümünde ComputeHash alternatifi yer alır.

C# ile Tek Dosyanın SHA256 Hash Değerini Alma

Temel yöntem dosyayı FileStream ile açar ve SHA256.HashData çağrısına verir. Böylece uygulama dosyanın tamamını tek seferde byte dizisine yüklemez. Akış yaklaşımı özellikle büyük video, PSD ve yedek dosyalarında bellek tüketimini dengeler.

using System.Security.Cryptography; static string CalculateSha256(string filePath) { using FileStream stream = new( filePath, FileMode.Open, FileAccess.Read, FileShare.Read ); byte[] digest = SHA256.HashData(stream); return Convert.ToHexString(digest); } string filePath = @"D:\DosyaArsivi\urun-fotografi.png"; string hash = CalculateSha256(filePath); Console.WriteLine(hash);

Burada FileMode.Open yalnızca var olan dosyayı açar. FileAccess.Read yazma girişimini engeller. Ayrıca FileShare.Read başka süreçlerin aynı dosyayı okumasına izin verir. Convert.ToHexString büyük harfli, 64 karakterlik bir metin döndürür. Örneğin boş dosyanın SHA-256 değeri E3B0C44298 ile başlar. Bu yüzden sıfır baytlık dosyalar da aynı içerik grubu oluşturabilir.

C# İki Dosyanın Aynı İçeriğini Nasıl Kontrol Eder?

İki dosyayı karşılaştırırken kod önce boyutları denetler. Boyutlar farklıysa içerikler de farklıdır ve uygulama hash hesabını atlar. Ardından uygulama eşit boyutlu dosyaların SHA-256 değerlerini karşılaştırır.

static bool HaveSameContent( string firstPath, string secondPath) { var first = new FileInfo(firstPath); var second = new FileInfo(secondPath); if (first.Length != second.Length) { return false; } string firstHash = CalculateSha256(first.FullName); string secondHash = CalculateSha256(second.FullName); return StringComparer.Ordinal.Equals( firstHash, secondHash ); } bool same = HaveSameContent( @"D:\DosyaArsivi\sunum-final.pptx", @"D:\Yedek\sunum-yedek.pptx" ); Console.WriteLine(same ? "Aynı içerik" : "Farklı içerik");

Örnekte StringComparer.Ordinal kültür kurallarını devre dışı bırakır ve karakterleri doğrudan karşılaştırır. Böylece Türkçe harf dönüşümleri gibi yerel ayarlar hash kontrolünü etkilemez.

Çakışma notu:SHA-256 için teorik çakışma ihtimali vardır; pratik dosya temizleme araçlarında bu ihtimal son derece düşüktür. Kritik adli veya arşiv süreçleri hash eşleşmesinden sonra bayt bayt doğrulama ekleyebilir.

Klasördeki Aynı Dosyaları Bulan Tam C# Örneği

Gerçek uygulama binlerce dosyayı tarayabilir. Öncelikle Directory.EnumerateFiles dosya yollarını ihtiyaç oldukça üretir. Ardından kod dosyaları boyuta göre gruplar. Yalnızca en az iki üyeli boyut grupları SHA-256 aşamasına geçer. Böylece uygulama tek boyuta sahip dosyaları disktan okumaz. Ayrıca EnumerationOptions alt klasörleri tarar, uygulamanın erişemediği klasörleri atlar ve yeniden ayrıştırma noktalarını dışarıda tutar. Son ayar sembolik bağlantı döngülerine karşı ek koruma sağlar.

using System.Security.Cryptography; internal static class Program { public static void Main() { var groups = FindDuplicates( @"D:\DosyaArsivi" ); foreach (var group in groups) { Console.WriteLine( $"\n{group.Size:N0} bayt | " + group.Sha256 ); foreach (string file in group.Files) { Console.WriteLine($" {file}"); } } } private static List FindDuplicates( string root) { if (!Directory.Exists(root)) { throw new DirectoryNotFoundException(root); } var options = new EnumerationOptions { RecurseSubdirectories = true, IgnoreInaccessible = true, AttributesToSkip = FileAttributes.ReparsePoint }; var sizeGroups = new Dictionary<long, List>(); foreach (string path in Directory.EnumerateFiles( root, "*", options)) { try { long size = new FileInfo(path).Length; if (!sizeGroups.TryGetValue( size, out var files)) { files = new List(); sizeGroups[size] = files; } files.Add(path); } catch (IOException ex) { Console.Error.WriteLine( $"Boyut okunamadı: {path} | " + ex.Message ); } catch (UnauthorizedAccessException ex) { Console.Error.WriteLine( $"Erişim reddedildi: {path} | " + ex.Message ); } } var hashGroups = new Dictionary< (long Size, string Hash), List >(); foreach (var candidates in sizeGroups.Values .Where(files => files.Count > 1)) { foreach (string path in candidates) { try { using FileStream stream = new( path, FileMode.Open, FileAccess.Read, FileShare.Read ); long currentSize = stream.Length; string hash = Convert.ToHexString( SHA256.HashData(stream) ); var key = (currentSize, hash); if (!hashGroups.TryGetValue( key, out var files)) { files = new List(); hashGroups[key] = files; } files.Add(path); } catch (IOException ex) { Console.Error.WriteLine( $"Hash üretilemedi: {path} | " + ex.Message ); } catch (UnauthorizedAccessException ex) { Console.Error.WriteLine( $"Erişim reddedildi: {path} | " + ex.Message ); } } } return hashGroups .Where(pair => pair.Value.Count > 1) .Select(pair => new DuplicateGroup( pair.Key.Size, pair.Key.Hash, pair.Value )) .OrderByDescending(group => group.Size * (group.Files.Count - 1)) .ToList(); } } internal sealed record DuplicateGroup( long Size, string Sha256, List Files );

Kod, hash hesabından önce FileStream.Length değerini yeniden okur. Çünkü başka bir program ilk taramadan sonra dosyayı değiştirmiş olabilir. Böylece grup anahtarı güncel boyut ile güncel hash değerini birlikte taşır. Son sıralama olası kazanımı öne çıkarır. Örneğin üç adet 100 MB dosya 200 MB tekrar alanı temsil eder; çünkü kullanıcı bir özgün dosyayı koruyup iki kopyayı değerlendirebilir. Ancak uygulama otomatik silme işlemi başlatmamalıdır.

C Ile Ayni Dosyalari Bulma3 Bsoylu

SHA-256 taramasında bulunan aynı içerikli dosya grupları

Sonuçları JSON Dosyasına Aktarma

Tarama sonucunu JSON biçiminde saklamak inceleme ve raporlama işini kolaylaştırır. Ayrıca System.Text.Json .NET ile birlikte gelir. Aşağıdaki kod, tarayıcının bulduğu grupları düzenli girintilerle kaydeder.

using System.Text.Json; var jsonOptions = new JsonSerializerOptions { WriteIndented = true }; string json = JsonSerializer.Serialize( groups, jsonOptions ); File.WriteAllText( "ayni-dosyalar.json", json );

Bunun yanında rapor dosya silme kararından önce kayıt sağlar. Kullanıcı hangi yolların aynı gruba girdiğini görebilir ve yanlış klasör seçimini fark edebilir.

Büyük Dosyalarda Asenkron SHA256 Kullanımı

Masaüstü veya web uygulaması uzun disk işlemi sırasında kullanıcı arayüzünü kilitlememelidir. Bu nedenle SHA256.HashDataAsync akış tabanlı asenkron hesap sunar. CancellationToken ise kullanıcıya taramayı durdurma imkânı verir.

static async ValueTask CalculateSha256Async( string filePath, CancellationToken cancellationToken = default) { var options = new FileStreamOptions { Mode = FileMode.Open, Access = FileAccess.Read, Share = FileShare.Read, Options = FileOptions.Asynchronous | FileOptions.SequentialScan }; await using var stream = new FileStream( filePath, options ); byte[] digest = await SHA256.HashDataAsync( stream, cancellationToken ); return Convert.ToHexString(digest); }

Asenkron yöntem işlemci hesabını sihirli biçimde hızlandırmaz. Buna karşılık disk okumasını bekleyen iş parçacığını serbest bırakır. Böylece WinForms, WPF ve ASP.NET Core uygulamaları daha akıcı tepki verir. Ayrıca FileOptions.SequentialScan işletim sistemine dosyayı baştan sona okuyacağınızı bildirir. İşletim sistemi bu ipucunu önbellek davranışını iyileştirmek için kullanabilir.

WinForms Uygulamasına Nasıl Eklenir?

WinForms tarafında FolderBrowserDialog kullanıcıdan taranacak klasörü alır. Ardından Task.Run işlem yoğun taramayı arka planda yürütür. Böylece form donmaz ve kullanıcı pencereyi taşımaya devam eder.

private async void btnKlasorSec_Click( object sender, EventArgs e) { using var dialog = new FolderBrowserDialog(); if (dialog.ShowDialog() != DialogResult.OK) { return; } btnKlasorSec.Enabled = false; Cursor = Cursors.WaitCursor; listBoxSonuclar.Items.Clear(); try { var groups = await Task.Run( () => FindDuplicates(dialog.SelectedPath) ); foreach (var group in groups) { listBoxSonuclar.Items.Add( $"{group.Files.Count} dosya | " + $"{group.Size:N0} bayt" ); foreach (string path in group.Files) { listBoxSonuclar.Items.Add($" {path}"); } } } catch (Exception ex) { MessageBox.Show( ex.Message, "Tarama hatası", MessageBoxButtons.OK, MessageBoxIcon.Error ); } finally { Cursor = Cursors.Default; btnKlasorSec.Enabled = true; } }

Örnekte form yalnızca sonuçları listeler. Bir silme düğmesi eklediğinizde uygulama her grup için kullanıcının koruyacağı dosyayı açıkça göstermelidir. Ayrıca kullanıcı onayı, geri dönüşüm kutusu ve işlem günlüğü veri kaybı riskini azaltır.

Eski .NET Framework Projesinde SHA256 Kullanımı

Eski projeler statik SHA256.HashData metodunu sunmayabilir. Bu durumda SHA256.Create ve ComputeHash aynı amacı görür. Ayrıca using bloğu algoritma nesnesini işlem sonunda kapatır.

static string CalculateSha256Legacy(string filePath) { using (FileStream stream = File.OpenRead(filePath)) using (SHA256 sha256 = SHA256.Create()) { byte[] digest = sha256.ComputeHash(stream); return BitConverter .ToString(digest) .Replace("-", string.Empty); } }

Performansı Artıran Uygulamalar

Disk hızı toplam süreyi büyük ölçüde belirler. Bu nedenle her dosya için doğrudan SHA-256 üretmek yerine önce boyut gruplaması yapın. Özellikle fotoğraf ve video arşivlerinde bu adım binlerce gereksiz okuma işlemini kaldırabilir.

  1. EnumerateFiles ile yolları ihtiyaç oldukça alın.
  2. Length ile yalnızca aynı boyuttaki dosyaları aday yapın.
  3. ReadAllBytes yerine FileStream kullanın.
  4. Ağ sürücülerinde sınırlı paralellik uygulayın ve bağlantıyı aşırı yüklemeyin.
  5. Dosya değişikliklerini yakalamak için hash öncesinde güncel stream uzunluğunu alın.
  6. Erişim hatalarını kayıt altına alın; tüm taramayı tek dosya yüzünden durdurmayın.
  7. Kullanıcıya dosya sayısı, okunan bayt ve tamamlanma yüzdesi gösterin.

Buna karşılık sınırsız paralel hash hesabı çoğu bilgisayarda hız kazandırmaz. Birden fazla görev aynı diske yöneldiğinde kafa hareketi, kuyruk ve ağ gecikmesi artabilir. Bu yüzden SSD üzerinde bile küçük bir eşzamanlılık sınırıyla ölçüm yapın.

Hata Yönetiminde Hangi Noktalara Dikkat Etmelisiniz?

Dosya taraması sırasında antivirüs, senkronizasyon uygulaması veya kullanıcı bir dosyayı taşıyabilir. Ayrıca bazı klasörler okuma izni vermeyebilir. Bu nedenle kod dosya bazında IOException ve UnauthorizedAccessException yakalamalıdır.

SorunOlası nedenÖnerilen davranış
Dosya kaybolduTarama sırasında taşıma veya silmeDosyayı atlayın ve yolu günlüğe ekleyin.
Erişim reddedildiKlasör ya da dosya izniTaramayı sürdürün ve kullanıcıyı bilgilendirin.
Dosya değiştiSenkronizasyon veya açık uygulamaGüncel boyutu stream üzerinden yeniden okuyun.
Tarama yavaşAğ diski veya çok büyük dosyalarBoyut ön elemesi ve ilerleme göstergesi kullanın.
Yanlış silme riskiOtomatik kararOnay alın ve geri dönüşüm kutusu kullanın.

 

SHA256 ile Dosya Silmek Güvenli mi?

Hash eşleşmesi güçlü bir içerik kanıtı sunar; yine de uygulama doğrudan kalıcı silme yapmamalıdır. Öncelikle kullanıcı her grubu incelemeli ve korunacak ana dosyayı seçmelidir. Ardından yazılım mümkünse kopyaları geri dönüşüm kutusuna taşımalıdır. Özellikle klasör konumu karar sürecini etkiler. Örneğin aynı fotoğraf hem proje klasöründe hem düzenli yedek yapısında bulunabilir. İçerik aynı olsa da iki konum farklı iş amaçlarına hizmet edebilir.

Sık Sorulan Sorular

SHA-256 aynı dosyayı kesin olarak bulur mu?

Pratik uygulamalarda aynı boyut ve aynı SHA-256 değeri son derece güçlü bir eşleşme sunar. Bununla birlikte teorik çakışma ihtimali sıfır değildir. Kritik süreçler son aşamada bayt bayt karşılaştırma ekleyebilir.

Dosya adı değişirse hash değeri değişir mi?

Hayır. Çünkü SHA-256 dosya adını değil, dosyanın içindeki baytları işler. Bu nedenle farklı ad taşıyan kopyalar aynı hash değerini üretir.

Dosyanın tarihi değişirse SHA-256 değişir mi?

Dosya içeriği aynı kalırsa oluşturma ve değiştirme tarihi hash değerini etkilemez. Ancak dosya formatı meta veriyi içeriğe yazarsa ilgili baytlar hash değerini değiştirir.

MD5 neden doğru seçim değildir?

Yeni projelerde SHA-256 daha güçlü bir varsayılan sunar. Ayrıca Microsoft, MD5 algoritmasını bütünlük ve güvenlik odaklı kullanımlarda önermiyor.

Büyük dosya belleği doldurur mu?

FileStream kullanan örnek dosyayı parça parça okur. Bu nedenle uygulama tüm içeriği tek byte dizisine almaz. Hash çıktısı yalnızca 32 bayt yer kaplar.

Boş dosyalar aynı içeriğe sahip midir?

Evet. Tüm sıfır baytlık dosyalar aynı içeriği taşır ve aynı SHA-256 değerini üretir. Uygulama isterse bu grubu ayrı bir seçenekle gizleyebilir.
Sonuç olarak C# ile aynı dosyaları bulma işlemi üç güçlü adıma dayanır. Uygulama önce dosyaları tarar, ardından aynı boyuttaki adayları seçer ve son olarak SHA-256 değerlerini karşılaştırır. Bu yapı farklı adlara sahip gerçek kopyaları güvenilir biçimde ortaya çıkarır. Üstelik System.Security.Cryptography ek paket gerektirmeden modern .NET projelerinde çalışır. Kısacası akış tabanlı okuma, kontrollü hata yönetimi ve kullanıcı onayı hem performansı hem veri güvenliğini güçlendirir.

BSoylu
BSoylu

Debug ettikçe güzelleşen bir dünya için..

Yorum Ekle

İLGİNİZİ ÇEKEBİLİR
WordPress’te ‘Error Establishing a Database Connection’ Hatası ve Çözümü
23 Kasım 2024

WordPress’te ‘Error Establishing a Database Connection’ Hatası ve Çözümü

C# ile Aynı Dosyaları Bulma

Bu Yazıyı Paylaş

İnternet sitemizde tanıtım yazınız olmasını ister miydiniz? İletişim
Bildirimler
1