Pazar, Ağustos 13, 2006

Delphi'de Dosya Arama Fonksiyonları

Bu yazıda Delphi ile dosya aramayı göreceğiz. Burada yazacağımız fonksiyon bir bilgisayar üzerinde bulunan dosyaları aramaktan çok programınızın bir klasör içindeki dosyaların listesini vermesini sağlamak için kullanılabilir. Çünkü dosya sayısı arttığında oldukça yavaşlıyor. Bu örneğin "plug-in" desteğine sahip bir program yaptığınızda plugin klasörünün içindeki "dll" dosyalarının listesini almak için kullanabilirsiniz.

Delphi'de arama yapmak için "TSearchRec" adındaki bir kayıt tipini ve "FindFirst,FindNext,FindClose" fonksiyonlarını kullanıyoruz. Aynı zamanda burada "repeat-until" döngüsünü de göreceğiz ve kullanacağız. Bu döngü aslında while döngülerinin farklı bir versiyonu ancak okunabilirlik ve anlaşılabilirlik açısından daha pratik.

Öncelikle fonksiyonumuzun tanımını yapalım:

function DosyalariBul(yol, aramametni: string): integer;

Burada yol ve aramametni olmak üzere 2 parametre tanımladık. Bunlardan "yol" tahmin edebileceğiniz gibi arama yapılacak yolu belirtirken "aramametni" de Windows'tan aşina olduğumuz "*" karakterinin de kullanabileceğimiz bir arama metnini temsil ediyor. Bunlara ek olarak fonksiyonun başında "bulunan" adında ve "TSearchRec" tipinde bir değişken daha tanımlıyoruz. Bu değişken bulunan dosyaya ait bilgileri tutacak.

var
bulundu: TSearchRec;

İşte şimdi "FindFirst" fonksiyonunu kullanmamız gerekiyor. Burada ufak bir açıklama yapalım hemen. FindFirst bir aramaya başlarken başlangıçta ve sadece bir kere çağırmamız gerekn bir fonksiyondur. Bu fonksiyon parametre olarak aramametni ve yolun birleştirilmiş bir versiyonunu, bulunacak dosyaların özelliklerini(arşiv, gizli, klasör vs.) ve bulundu adlı kaydımızı parametre olarak alıyor. Dönüş değeri "0" ise kriterlerimize uygun bir veya daha fazla dosya bulunduğunu anlıyoruz. Daha sonra kriterimize uyan bir sonraki dosyayı bulmak için FindNext fonksiyonunu çağırıyoruz. Bu fonksiyon sadece "bulundu" adlı değişkenimizi parametre alıyor. Arama kriterlerini ise FindFirst ile bir kez belirttiğimiz için tekrar vermemize gerek yok. Yine bu fonksiyondan da dönüş değeri olarak "0" değerini alırsak aramamıza devam edebiliriz. O zaman buna uygun bir "repeat-until" döngüsü şu şekilde olacak:

if FindFirst(yol+aramametni,faAnyFile,bulundu) = 0 then
begin
repeat
begin

//buraya bulunan dosyayla ilgili yapılacak işlemler girecek
end;
until (FindNext(bulundu) <> 0);
end; //if'in end'i.

Bu kısmı incelersek, ilk başta ğer kriterimize uygun bir dosya yoksa aramaya girmememizi sağlayan bir IF komutu yer alıyor. Bunun ardından FindNext fonksiyonundan 0'dan farklı bir değer dönene kadar; yani uygun başka dosya kalmayana kadar yukarıda belirtilen(repeat ve end arasında kalan) komutları uygula demiş oluyoruz. Bu arada unutmadan "yol" değişkenine girilen değerin sonunda "\" işaretinin olması gerekiyor ki sonuna arama metnini eklediğimizde sorun yaşanmasın.

Bizim fonksiyonumuzun dönüş değer tipi integer, çünkü biz sadece bulunan dosya sayısını döndüreceğiz. Bu yüzden fonksiyonun başında dönüş değerini sıfırlamamız gerekiyor:

result:=0;

Şimdi yukarıda boş bıraktığımız yere

inc(result);

komutunu eklersek fonksiyonumuz işimizi görecek hale geliyor. Yalnız fonksiyonun bitiminden hemen önce

FindClose(bulundu);

yazmamız gerekiyor ki "bulundu" değişkenimiz için ayrılan bellek serbest bırakılsın.

Tabi ki bu fonksiyonu kullanacak kişiler sadece dosya sayısını istemeyecekler. O zaman biraz daha detaya girelim ve "TSearchRec" tipinin dosyaya ait ne gibi bilgiler içerdiğine bir göz atalım. Bu bir kayıt tipi olduğu için içinde altbirimler var. Bu birimlerin isimleri, tipleri ve ne gibi bilgiler içerdikleri aşağıda:

Time: Integer;
Bu, adından da anlaşılacağı üzere dosyanın zaman ve tarih bilgisini tutuyor. Bu bilgi dosyanın son değiştirilme tarihini Delphi'nin zaman formatında saklıyor.

Size: Integer;
Yine adından anlaşılacağı üzere bu da dosyanın boyut bilgisini içeriyor. Yalnız burada dikkat edilmesi gereken nokta bu bilginin kesin doğru olmayabileceği. Çünkü artık doya boyutları 64-bit'lik sayıalr da olabiliyorlar ve bu "Integer" tipinde yani 32-bit'lik bir değişken.

Attr: Integer;
Bu değişken ise dosyanın öznitelik bilgisini saklıyor. Integer tipinde tanımlanmış olmasına rağmen Delphi size anlaşılır isimlere sahip sabitler sunuyor. Yani gizli dosyanın öznitelik değeri kaçtı diye hatırlamanıza gerek kalmıyor. Buraya gireceğiniz sabitleri veya değerleri "FindFirst" fonksiyonunun dosya tipi böümünde de kullanabilirsiniz. Şimdibu sabitlere bir bakalım:

faReadOnly : Bu "Salt Okunur" özniteliğini temsil ediyor. Sayısal değeri "1".
faHidden : Bu ise "Gizli" özniteliğini temsil ediyor. Sayısal değeri "2".
faSystem : "sistem Dosyası" özniteliğni temsil eder. Sayısal değri "4".
faVolumeID : "Sürücü tanılama dosyası" özniteliğini temsil eder. Sayısal değeri "8".
faDirectory : Bulunan öğenin bir klasör olduğunu belirtir. Sayısal değeri "16".
faArchive : "Arşiv" özniletiliğini belirtir. Sayısal değeri "32".
faSymLink : Bulunan öğrenin sembolikbir bağlantı olduğunu gösterir(ben de bilmiyorum ne olduğunu :)). Sayısal değeri "64"
faAnyFile : Bunu bulunan dosyalara bakarken kullanmaktan çok arama yaparken her tipteki dosyanın buunmasını sağlamak için FindFirst fonksiyonunda kullanmak bir anlam ifade ediyor. Her türlü dosya tpini temsil ediyor. Sayısal değeri "71".

Şimdi burada o kadar sabit değerden bahsettim flan ama biz bulduğumuz öğenin buözelliklere sahip olup olamdığın nasıl anlayacağız? Şöyle ki eğer "Attr" bilgisini istediğimiz özelliğin değeri ile "AND" işleminden geçirdiğimizde sonuç sıfırdan büyük oluyorsa bu özellik öğede var demektir. Bir örnekle daha da açıklığa kavuşturalım olayı:

if (bulundu.Attr AND faDirectory) > 0 then ....

Burada eğer bulunan öğre bir klasör ise "then" kısmından sonragelen komut(lar) çalıştırılır. Sanırım yeteri kada açıklayıcı oldu. Bu işlemi sadece bir sabitle değil birden çok sabitle de yapabilirsiniz. (bulundu.Attr AND faDirectory AND FaArchive; gibi).

Name: TFileName;
Tipinin TFileName olmasına bakmayın o aslında bir string :). Tek farkı bu tipin sadece dosya isimleri için kullanılan özel bir string türü olması. Yani anlayacağınız bulunan öğenin adı burada saklanıyor. Yanlız unutmayın burada SADECE adı var, yol bilgisi yok.

Evet işte bitirdik. Son olarak bu fonksiyon arama yaptığınız klasörün alt klasörlerine bakmaz. Eğer bunu yapmasını istiyorsak o zaman fonksiyonumuzun kendi kendisini çağırmasını sağlamalıyız. Buna dabir örnek yazalım ve bu yazımıza da son noktayı yada noktalı virgülü(!) koyalım :).

function DosyalariBul(yol,aramametni:string):integer;
var
bulundu:TSearchRec;
begin
result:=0;
if FindFirst(yol+aramametni,faAnyFile,bulundu) = 0 then
begin
repeat
begin

if (bulundu.Attr AND faDirectory) > 0 then
result:=result+DosyalariBul(yol+buldu.Name+'\',aramametni)
else
inc(Result);
end;
until
(FindNext(bulundu) <> 0);
end;
FindClose(bulundu);
end;

Fonksiyonumuzun son hali böyle olmalı. Burada gördüğünüz gibi önce öğenin klasör olup olmadığına bakılıyor; eğer klasör ise bu sefer fonksiyonumuzdan bir tane daha çağırıyoruz. Burada fark yeni çağırılan haline parametre olarak ilk yolun değil ilk yol + bulunan klasör'ün verilmesi. Daha sonra buradan dönen dosya sayısını ana sayacımıza ekliyoruz. Burada dikkat etmenizi istediğimbir nokta daha var; fonksiyonumuz bu haliyle bir klasörün içindeki TÜM altklasörlere kadar inebiliyor. Çünkü yeni parametrelerle çağırılan fonkiyonumuz bir klasör bulduğunda kendini daha yeni parametrelerle tekrar çağırıyor. Burada kafanızın karışmaması için fonksiyonu her çağırdığımızda o çağrıya özel bir bellek alanını kullandığını unutmamanız. Yani çağrıyı yapan fonksiyondan bağımsız bir şekilde çalışıyor. Sadece işlettikleri komutlar aynı. Neyse çok dağıldık, en son klasörse bunlar bunları yapıyor dedik, eğer klasör değilse o zaman da sonucmuzu bir arttırıyoruz.

Not: Yazı, zamanında ByteOnline için yazdığım "Delphi-Dosya Arama Fonksiyonları" adlı yazıdan uyarlamadır.