Bir programın en başından nasıl yapıldığını anlatmaya devam ediyorum. Geçen yazımda, basit bir dosya adlandırma programının arayüz tasarımını yapmıştım. Şimdi de programın kodlarını yazmayı anlatacağım.Programın işlevsel hale gelmesi için ilk aşama, Listele düğmesinin çalışmasıdır. Bunun için de bir klasördeki dosya adlarına nasıl erişebileceğimi bilmek gerekiyor. İnternette yaptığım arama sonucunda şu sayfaya ulaşarak bu işi nasıl yapacağım hakkında bilgiye ulaştım.Form tasarımıma dikkat ettiyseniz, hangi klasördeki dosya isimlerin ListBox’ta gösterileceği ile ilgili bir ayar olmadığını fark etmişsinizdir. Klasör seçimi için bir düğme koymak yerine, programın exe dosyası hangi klasördeyse, oradaki dosyaların listelenmesini istediğim için, forma klasör seçilebilecek bir düğme koymadım. Programın hangi klasörde çalıştığını öğrenmek için de, önceden yazmış olduğum aşağıdaki fonksiyonu kullanmaya karar verdim.function GecerliKlasoruAl: string;beginResult := ExtractFilePath(Application.ExeName);if Result[Length(Result)] <> ” thenResult := Result + ”;end;
Yine yazacağım kodda sık sık bir karakterin rakam mı, yoksa başka bir karakter mi olduğuna karar vermek gerekiyordu. Bu nedenle de aşağıdaki fonksiyonu yazdım.function rakammi(karakter: string): boolean;beginif (karakter[1]=’0′) or (karakter[1]=’1′) or (karakter[1]=’2′) or (karakter[1]=’3′) or (karakter[1]=’4′) or (karakter[1]=’5′) or (karakter[1]=’6′) or (karakter[1]=’7′) or (karakter[1]=’8′) or (karakter[1]=’9′) thenresult:=Trueelseresult:=False;end;
Delphi string’lere bir dizi gibi ulaşma imkanı verdiği için, yukarıdaki fonksiyonu basit bir şekilde yazabildim.Programda ihtiyacım olacak başka bir değer de, dosya adında kaç tane sayı grubu olduğuydu. Bunu bulabilmek için de aşağıdaki fonksiyonu yazdım.function sayigrububul(metin: string): byte;vari: integer;sonuc: byte;oncekikarakterrakammi: boolean;beginoncekikarakterrakammi:=false;for i:=1 to length(metin) doif rakammi(metin[i]) thenbeginif oncekikarakterrakammi=false thenbegininc(sonuc);oncekikarakterrakammi:=true;end;endelseoncekikarakterrakammi:=false;result:=sonuc;end;
Bu kodu biraz açıklamakta fayda var. Örnek bir dosya adı üzerinden gidelim. Diyelim bir dosyanın adı a11b2.avi olsun. Burada iki tane sayı grubu olduğunu açıkça görüyoruz. Bilgisayara bunu tespiti yaptırmak için şu yolu izleyebiliriz. Verilen metinin tüm karakterlerine sırasıyla bak. Bir rakam bulduysan sonuç değeri bir arttır ve sıradaki karaktere geç. Bir yerde de rakam bulduğunu tut. Sonraki karakterde rakamsa bir önceki bulduğunda rakam mı bak. Eğer rakamsa sonuç değeri arttırma, rakam değilse, yeni bir sayı grubu buldun demektir, sayı değerini arttır.Artık Listele düğmesi için gereken kodu yazmaya hazır sayılırız. Ancak burda bir noktayı daha göz önüne almalıyız. İncelenecek klasördeki dosyalarımızın adlarındaki sayı grubu sayısı birbirinden farklıysa program ne yapacak? Durumu biraz somutlaştırayım. Diyelim ki klasördeki bir dosyanın adı diziadi.bolum.46.uploader.avi, ikinci dosyanın adı da a11b2.avi olsun. Bu durumda programımız hiçbir şey yapmasın. Bunu sağlayabilmek için de yeni bir alt programa ve iki tane global değişkene ihtiyacımız var.Yazacağım sayigruplarikontrol isimli alt program, ListBox’taki tüm dosya adlarına bakacak ve her birindeki sayı grubunun eşit mi olduğunu kontrol edecek. Eğer bütün sayı grupları eşitse bir problem yok, ancak eşit değilse alt program mantıksal bir değişkene yanlış değerini atamalı.Öncelikle listedekitumsayigruplariaynimi ve oncekisayigrubu grubu isimli iki tane global değişken tanımladım. Hem bu değişkenlere ilk değerleri atamak, hem de klasör incelenmeden iş yapacak düğmeleri aktif hale getirmemek için, program çalıştırıldığında ilk yapılacak işlemlerden biri olan form oluşturma işlemine, aşağıdaki kodu yazdım.procedure TForm1.FormCreate(Sender: TObject);beginrdg1.Enabled:=false;btnAdlandir.Enabled:=false;lst1.Enabled:=false;oncekisayigrubu:=-1;listedekitumsayigruplariaynimi:=True;end;
sayigruplarikontrol isimli alt programın kodları ise şu şekildeydi:procedure sayigruplarikontrol(dosyaadi: string);beginif dosyaadi=ExtractFileName(Application.ExeName) thenexit;if (listedekitumsayigruplariaynimi=False) thenexit;if oncekisayigrubu<0 thenoncekisayigrubu:=sayigrububul(dosyaadi)else if oncekisayigrubu<>sayigrububul(dosyaadi) thenlistedekitumsayigruplariaynimi:=false;end;
Bu kodu da biraz açıklamak gerekirse, 3. satırdaki kod listede programımızın kendi ismi varsa, bu isim sayı içeremeyeceğinden, alt programdan çıkılmasını sağlıyor. Eğer listedeki sadece bir elemanın bile sayı grubu sayısı diğelerinden farklıysa, alt programın kontrol yapmasına gerek kalmıyor. Bunu da 5. satır sağlıyor. Program ilk çalıştığında oncekisayigrubu değişkenine negatif değer atadığımızdan, 7. satır program ilk kez çalıştıysa oncekisayigrubu global değişkenine karşılaştırmalarda kullanılacak ilk değerini atıyor. Program ilk kez çalışmıyorsa, önceki sayı grubu sayısı ile yeni sayı grubu sayısı karşılaştırılıyor ve bu iki eğer farklı ise program listedekitumsayigruplariaynimi değişkenine yanlış değerini atıyor.Artık Listele düğmesinin koduna geldik. Bu düğmeye aşağıdaki kodu yazıyoruz.procedure TForm1.btnListeleClick(Sender: TObject);varbulunan:TSearchRec;beginlst1.Enabled:=True;lst1.Clear;if FindFirst(GecerliKlasoruAl+’*.*’, faAnyFile – faDirectory, bulunan)<>0 thenlst1.Items.Add(‘Klasör boş’)elsebeginlst1.clear;repeatlst1.items.Add(bulunan.name);sayigruplarikontrol(bulunan.Name);until FindNext(bulunan)<>0;FindClose(bulunan);end;lst1.Items.Delete(lst1.Items.IndexOf(ExtractFileName(Application.ExeName)));lst1.Items.Delete(lst1.Items.IndexOf(‘log.txt’));if (listedekitumsayigruplariaynimi) thenbeginrdg1.Enabled:=True;btnAdlandir.Enabled:=True;case oncekisayigrubu of0: rdg1.Enabled:=False;1: beginrdg1.ItemIndex:=0;rdg1.Items.Delete(1);rdg1.Items.Delete(1);end;2: rdg1.Items.Delete(2);end;end;end;
Bu kodun 7. ve 15. satırlarında kullanılan FindFirst ve FindNext fonksiyonları bir klasörün içindeki dosya ve klasörleri bulmak için kullanılır. faAnyFile – faDirectory parametresi de klasörle dışında tüm dosyaların listesinin bulunmasını sağlar. Her iki fonksiyon da şartlara uyan bir dosya/klasör bulduğunda geriye 0 değerini döndürür.Bu bilgiler ışığında düğmemize yazdığımız kodu açıklayalım. Öncelikle bulunan isimli bir değişken tanımlıyoruz. 5. satırda ListBox’ı kullanılabilir hale getiriyoruz. 6. satırda ListBox’ın içini temizleyip, 7. satırda ilk aramayı yapıyoruz, eğer dosya bulunmadıysa listeye Klasör boş mesajını ekliyoruz. Klasör boş değilse, bulunan dosyanın adı ListBox’a ekleniyor. 12. ve 15. satırlar arasındaki döngü ile şartı sağlayan tüm dosyalar ListBox’a ekleniyor. 14. satır ile de ListBox’a eklenen dosyaların isimleri içinde aynı sayıda sayı grubu olup olmadığı kontrol ediliyor. Bulunan dosya isimleri içinde programımızın kendi adı de yer alacaktır. Bu ismi silmek için ListBox’ın içinde Items.IndexOf özelliği ile programın kendi dosya adını arattırıp, ListBox’tan sildiriyoruz. Aynı işlemi sonradan oluşturacağız log.txt dosyası için de yapıyoruz.20. satırda listedeki tüm sayı grupları aynı ise, radyo grubu ve adlandır düğmeleri aktif hale getiriliyor. 24. satırdaki Case ifadesi ile de radyo grubunun hangi elemanlarının ekranda görülenceği ayarlanıyor. Eğer dosya adlarında sadece 1 sayı grubu varsa, radyo grubunda da sadece ilk seçenek kalıyor, diğer seçenekler siliniyor.Şimdi asıl işi yapacak olan Adlandır düğmesine geldik. Bu düğmeye ait kodu yazmadan evvel de birkaç fonksiyon yazmak işimizi kolaylaştıracak. Bu fonksiyonlardan ilki, dosya adından, dosya uzantısını elde etmemizi sağlayan aşağıdaki fonksiyon.function uzantibul(dosyaadi: string): string;varuzunluk, i: integer;beginuzunluk:=length(dosyaadi);for i:=uzunluk downto 1 doif dosyaadi[i]=’.’ thenbeginresult:=copy(dosyaadi, i+1, uzunluk-i);break;end;end;
Bu fonksiyon, dosya isminin sonundan başlayarak . karakterini arar. Bu karakteri bulduktan sonra, bulduğu pozisyonun bir sonrasında başlayıp, dosya isminin sonuna kadar olan karakterleri geri döndürerek dosyanın uzantısını bulmayı sağlar.Bir değer fonksiyonumuz da boşlukekle isimli aşağıdaki fonksiyon. Bu fonksiyon da kendsine verilen ‘1′ değerini ‘001′ haline getirmeye yarıyor.function boslukekle(hane: integer): string;vari: integer;tut: string;beginfor i:=1 to hane dotut:=tut+’0′;result:=tut;end;
Ve şimdi de programda yazarken en çok vakti harcadığım yeniadbul isimli fonksiyona geldik. Bu fonksiyonu üç parça halinde anlatmak istiyorum, çünkü anlatması biraz karışık olan bir fonksiyon.yeniadbul isimli fonksiyon, eskiad ve sayigrubu olarak iki parametre almakta. sayigrubu parametresi 0 değerin aldığında, yani dosya adında sadece 1 sayısal ifade varsa fonksiyon yeni adı şu şekilde üretiyor.for i:=1 to length(eskiad) doif rakammi(eskiad[i])thenbeginilkrakambulundumu:=true;biriktir:=biriktir+eskiad[i];endelse if (ilkrakambulundumu) thenbeginresult:=boslukekle(3-length(biriktir))+biriktir;exit;end;
Bu kodun yaptığı iş şu: Eski adın tüm karakterlerine tek tek bakıyor. Eğer bir rakamla karşılaşırsa, rakam bulduğunu bir yerde tutuyor ve bulduğu rakamı biriktirmeye başlıyor. Daha sonra rakam olmayan bir karakterle karşılaştığında, biriktirdiği değerin soluna sıfır ekleyip geri döndürüyor.sayigrubu parametresi 1 değerini aldığında yani dosya adında 2 sayısal ifade varsa, işler biraz karışıyor.i:=0;while True dobegininc(i);if rakammi(eskiad[i]) thenbeginwhile True dobegininc(i);if not rakammi(eskiad[i]) thenbeginwhile True dobegininc(i);if rakammi(eskiad[i]) thenbeginbiriktir:=biriktir+eskiad[i];endelsebeginresult:=boslukekle(3-length(biriktir))+biriktir;exit;end;end;endend;endelseinc(i);end;
Burada da yazılan kod, eski adın karakterlerine tek tek bakarak rakam arıyor. Bu rakamı bulduğunda ise bu sefer rakam olmayan karakteri arıyor. Rakam olmayan karakateri bulduğunda ise ikinci sayı grubunu tespit edebilmek için tekrar rakam aramaya başlıyor. Rakamı bulduktan sonra, bulduğu rakamı biriktiriyor ve rakam olmayan bir karakterle karşılaştığında, biriktirdiği değerin soluna sıfır ekleyip geri döndürüyor.sayigrubu parametresi değeri 2 değerini aldığında ise aşağıda gördüğünüz kod çalıştırılıyor.i:=length(eskiad)+1;while True dobegindec(i);if rakammi(eskiad[i]) thenbeginwhile True dobegindec(i);if not rakammi(eskiad[i]) thenbeginwhile True dobegininc(i);if rakammi(eskiad[i]) thenbeginbiriktir:=biriktir+eskiad[i];endelsebeginresult:=boslukekle(3-length(biriktir))+biriktir;exit;end;end;end;end;endelsedec(i);end;
Bu bölümde ise rakam aramaya sondan başlanıyor. Eski adın en son karakterine bakıloyor ve rakamı bulana kadar bir önceki karaktere bakarak gerileniyor(sola doğru). İlk rakamı bulduktan sonra da rakam olmayan karakteri aramaya başlıyoruz(sola doğru). Rakam olmayan ifadeyi de bulduktan sonra bu sefer ilerleyip (sağa doğru) bulunan rakamları biriktiriliyor ve rakam olmayan bir karakter ile karşılaşıldığında, biriktirlen rakamlar soluna sıfır eklenip geri döndürülüyor.Bölüm bölüm açıkladığım bu fonkiyonun tamamı aşağıda yer almaktadır.function yeniadbul(eskiad: string; sayigrubu: integer): string;vari: integer;biriktir: string;ilkrakambulundumu: boolean;beginbiriktir:=”;ilkrakambulundumu:=false;case sayigrubu of0: beginfor i:=1 to length(eskiad) doif rakammi(eskiad[i])thenbeginilkrakambulundumu:=true;biriktir:=biriktir+eskiad[i];endelse if (ilkrakambulundumu) thenbeginresult:=boslukekle(3-length(biriktir))+biriktir;exit;end;end;1: begini:=0;while True dobegininc(i);if rakammi(eskiad[i]) thenbeginwhile True dobegininc(i);if not rakammi(eskiad[i]) thenbeginwhile True dobegininc(i);if rakammi(eskiad[i]) thenbeginbiriktir:=biriktir+eskiad[i];endelsebeginresult:=boslukekle(3-length(biriktir))+biriktir;exit;end;end;endend;endelseinc(i);end;end;2: begini:=length(eskiad)+1;while True dobegindec(i);if rakammi(eskiad[i]) thenbeginwhile True dobegindec(i);if not rakammi(eskiad[i]) thenbeginwhile True dobegininc(i);if rakammi(eskiad[i]) thenbeginbiriktir:=biriktir+eskiad[i];endelsebeginresult:=boslukekle(3-length(biriktir))+biriktir;exit;end;end;end;end;endelsedec(i);end;end;end;end;
Adlandır düğmemizin kodu da şu şekilde olmakta:procedure TForm1.btnAdlandirClick(Sender: TObject);vari: integer;tut: TStrings;isim: string;beginif lst1.Items.Count>0 thenbegintut:=TStringList.Create;tryfor i:=0 to lst1.Count-1 dotryisim:=yeniadbul(lst1.items[i], rdg1.ItemIndex)+’.’+uzantibul(lst1.items[i]);RenameFile(lst1.Items[i],isim);tut.Add(lst1.Items[i]+’ adlı dosyanın adı ‘+isim+’ olarak değiştirildi.’);excepttut.Add(lst1.Items[i]+’ adlı dosyanın adı ‘+isim+’ olarak değiştirilemedi!’);ShowMessage(‘Bir hata oluştu. Log dosyasını inceleyeniz.’);end;finallytut.SaveToFile(‘log.txt’);tut.Free;ShowMessage(‘İşlem Tamam!’);end;end;end;
7. satırda ListBox’ta eleman alıp olmadığı kontrol ediliyor. ListBox’ta eleman varsa 9. satırda geçici bir liste oluşturuluyor. Bu liste log tutmak için kullanılacak. Olası hatalara karşı, 10. satırda bir try bloğu oluşturuluyor. 11. satırda, ListBox’ın eleman sayısı kadar dönecek bir döngü oluşturuluyor. 13. satırda yeni dosya ismi oluşturuluyor. 14. satırda dosyanın adı değiştiriliyor. 15. satırda log listesine yapılan işlem kayıt ediliyor. Son olarak da log dosyası kaydediliyor ve kullanıcıya İşlem Tamam mesajı veriliyor.Son olarak da programımıza ufak bir ek yapalım. Listedeki herhangi bir eleman çift tıklandığında, o elemana verilecek isim bir mesaj olarak ekranda görünsün. Bunu yapabilmek için, ListBox’ın çift tıklama olayına aşağıdaki kod yazılır.procedure TForm1.lst1DblClick(Sender: TObject);beginif rdg1.Enabled thenShowMessage(yeniadbul(lst1.items[lst1.itemindex], rdg1.ItemIndex));end;
Programımız bitti. Programın exe dosyasına buradan ulaşabilirsiniz.