BASIC'TE DOSYALAMA
Retro bilgisayarBASIC'çiler sıkı durun başlıyoruz. Bu ayki konumuz dosyalamalar. Geçen aydan takip edenler hatırlayacaklar, dosyalamalarda karşılaşılan bazı sorunlara değinmiştik. Çözüm yollarını aramak ise bu aya kalmıştı. Kısaca tekrar edelim:
Amiga Basic ile kullanılabilen iki tür dosyalama var. Bunlardan birincisi ile dosyayı nasıl kaydetmişsek aynı şekil ve sırada geri alabiliyorduk. Sıralı veya Sequential adı verilen bu tür dosyalamanın hiç sorun çıkarmadan kullanılabildiğini anlatmış ve örnek vermiştik. İkinci tür dosyalama ise, bize bazı imkanlar sunuyordu. Random Access, rasgele erişim adı verilen bu dosyalama tekniği ile diskete belirli bir düzene (formata) uyan bilgi paketleri yerleştirebiliyor ve büyük bir dosya içerisinden istediğimiz pakete kolayca (o kadar kolay mı değil mi göreceğiz daha!) erişebiliyorduk. Bilgisayarınız ile basit programlar yapmak istiyorsanız, er veya geç bu tür dosyalama ile halledilebilecek programlar yazmak isteyeceksiniz. Bu tür ihtiyaçların en klasikleri: İsim listeleri, telefon defterleri veya stok defterleri tutmaktır.
Kağan iyi güzel de, geçen yazıda bize bir sürü sorun anlatmıştın diyeceksiniz. Doğru, ama sorunlarla karşılaşabilmek için ilk önce programımızın çatısını oluşturalım. Her zaman yaptığım gibi uzun dökümler vermekten kaçınmak istiyorum. Bu yüzden programı bölüm bölüm oluşturacağız. Tüm işlemleri GOSUB ile çağrılan ve RETURN ile biten bir altprogram şeklinde yazacağım.
Programımızla bir telefon defteri yapmak isteyelim. Basit olması açısından defterde sadece telefon sahibinin ismi, soyismi ve telefon numarası bulunsun. İlk belirlememiz gereken, dosyamıza ait bir bilginin ne şekilde ve ne büyüklükte temsil edileceği. Dosyamızda üç adet büyüklük var. Bunlara karar verdik. İsim ve soyisim mecburen alfanümerik olmak zorundalar. İşte ilk sorun, acaba bunlara ne büyüklük verelim. Yani isim ve soyisim kaçar harften oluşsun? Burada sorun bir şekil sorunu değil, bir yer sorunu. Örneğin isim 20, soyisim 40 karakterden oluşsun derseniz, toplam olarak bir dosyanızın bir elemanı 60 byte edecektir (numara işin içinde değil). Aynı eleman isim 8, soyisim 12 karakter alınırsa, 20 byte'lık yer kaplayacaktır. Burada 40 byte'ın ne önemi var diyeceksiniz. Unutmayın defterlerinizde 1'den çok fazla isim olmalı (yoksa niye defter tutuyorsunuz?). İlk bilgisayarım 1 Kb'lıktı (1024 byte), dolayısı ile tasarruflu olan isim 8 ve soyisim 12 karakter büyüklüğünü kullanacağım. Numara ikinci konumuz. Telefon numarasını bir sayı olarak alabilir veya isimler gibi karakter olarak tanımlayabiliriz, İstanbul'u düşünerek 338 08 95 olan numaramızı,
a$ = "3380895"
olarak 7 byte, veya
a =3380895
LSET a$ = MKL$(a) olarak 4 byte yer kaplayacak şekilde saklayabiliriz. İkinci örnekte gördüğünüz yöntemi, tüm sayıları saklamak için kullanmamız gerekiyor. Yani rasgele erişimli dosyalara sayılar yazamıyor, onları MKL$, MKI$, MKS$, MKD$ ile alfanümerik değişkenlere dönüştürüyor ve LSET veya RSET emirleri ile sola veya sağa bitiştiriyoruz. Bu tür bir sayıyı okumak için ise,
a = CVL (a$)
örneğinde olduğu gibi CVL, CVI, CVS ve CVD emirlerini kullanacağız. Ben tasarrufa devam ediyorum. Artık dosyamızı tanımlayabiliriz.
Dosya_ac:
OPEN D_ismi AS 1 LEN=24
FIELD #1,8 AS isim$, 12 AS soyisim$, 4
AS tno$
RETURN
Dosyamızın ismi D_ismi olarak tanımlandı. Bunu ister INPUT yolu ile kullanıcıdan sorar, ister bir sabit olarak kullanırsınız, size kalmış. Artık birim uzunluğu 24 olan ve bu 24 byte'ın 8'ini isim, 12'sini soyisim, 4'ünü ise telefon numarası için harcayan bir dosyaya sahibiz. Bu dosyalama türü ile kaç dosya mı açabiliriz? 1024 isim olsa, 24 kilobyte edecek. Bu kadar ufak bir dosya, 512 KB aletlerde bile kolayca RAM: diske sığabilir. Dikkat edin, birim uzunluk 200 byte olsa, bu dosya 200 KB tutacaktı!!!
Artık bir dosyamız var. Amacımız bu dosyaya veriler yazmak. Bu dosyanın özelliği, tüm dosyayı okumadan doğrudan istenen yere ulaşabilmek idi. Bu amaçla rasgele erişim dosyalarında bir numaralama kullanılır. İlk kayıta 1 numarası verilir ve her bir kayıt kendi numarası ile erişilir bir hale gelir. Burada önemli bir durumla karşı karşıyayız. KAYIT NUMARASINI BİLMEK ZORUNDAYIZ!!! Yani en azından son yazılan kayıtın numarasını bilmemiz lazım ki, yeni bir şey ekleyebilelim. Program ilk çalıştığında bu kolay. Dosya yeni olduğundan, doğrudan ilk dosyayı yazacağız. On gün sonra bir arkadaşınız daha olur ve numarasını eklemek istersek, en son nerede kaldığımızı bilmek zorundayız. İki yol önerebilirim: ilki her kaydın başında dosya numarasını sormak, son yapılan kaydı ise bir kenara not etmek. İkinci çözüm ise not etme işlemini bir sıralı dosya yardımı ile diskete yapmak ve gerektiğinde oradan okumak. İşte kayıt ekleme kısmımız:
Yeniisim:
OPEN Sonjsmi FOR INPUT AS 2
INPUT #2,numara
CLOSE #2
numara = numara +1
isimsor:
Print:Print
Print "Kayıt no : ", numara
Print
Print "Telefon Sahibinin "
Input "İsmi : ";iisim$
Input "Soyismi : ";isoyisim$
Input "Numarası : ";itno
Lset isimŞ = iisim$
Lset soyisim$ = isoyisim$
Lset tno$ = MkI$ (itno)
print
print "Bu bilgiler yanlışsa q tuşuna basın "
a$="1"
while a$= "" :a$=inkey$: wend
if UCASE$ (a$) = "Q" then goto isim_sor
put #1,numara
print
input " Yeni kayıt (evet/hayır)";a$
if Ucase$(Left$(a$,1)) <> "H" then
numara=numara+1
goto isimsor





end if
OPEN Son jsmi FOR OUTPUT AS 2
PRINT #2,numara
CLOSE #2
Return
Biraz uzunca oldu. Gelecek sefere süsleme mahiyetinde olan satırları eklemeyeceğim. Dikkat ederseniz, arada tuş sormak için iki ayrı basit yöntem kullandım. Tabii ki bu verileri okumak ve soruları sormak için çok daha etkili yöntemler kullanabiliriz. Ne var ki, bunlar şu anda konumuz değil (gelecek sefere süsleme tekniklerini anlatırız olur biter). Önemli olan parametremizi, yani dosyanın kaçıncı numarasında olduğumuzu diskete kaydedebildik. Ufak bir sorunumuz var. Programı ilk çalıştırdığımızda diskette verilen isimde bir dosya olmalı ve 0 sayısını içermeli (Numara bir arttırılıp kullanılıyor). Biraz önce ilk kullanım değil, ileri kullanımlar sorun yarattı. Şimdi ise ilk kullanım sorun yaratıyor. Kurtulmak için iki seçeneğimiz var. Biri, başka bir programla bu dosyayı ilk kullanımdan önce hazırlamak. İkinci yöntem ise, bu başka programı asıl programımızın bir parçası yapmak. Asıl programa parça yapmak olayı daha şık çözümlese de, sadece bir kere kullanılacak bir program bölümü ile programı aşırı kabartacaktır. Her halükarda ilk kullanımdan önce (yani ilk isim girilmeden),
OPEN Sonjsim FOR OUTPUT AS # 2
8=0
print #2,a
CLOSE #2
satırları çalıştırılmalıdır. Bu kısmı aslında sondan dosya silmek için de kullanabilirsiniz. Evet artık rahatça bilgi girebiliyoruz. Okuyamadığımız bilgiyi saklamanın anlamı yok. Hemen okuyabilecek bir düzen düşünelim.
İsim oku:
get #1,numara
iisim$ =isim$
isoyisimS = soyisim$
itno$ = tno$
gosub isim_yaz
return
isimyaz:
print numara;" Numaralı kayıt"
print " İsim : ";iisim$
print " Soyisim : ";isoyisim$
print " Numara : ";CVL(itno$)
return
Niye isimleri isim$'ten isim$'e kopyalıyorsun diye soracaksınız. Pek gerekli gözükmese de Field ile atanan değişkenler program içinde sabit kalmayan değişkenler. O yüzden mümkün olduğu kadar çabuk bu değerlerden asıl değerleri almak gerekir. Belki ileri aşamalarda bu okunan değerlere, ekrana yazmak dışında ihtiyacımız olur. Dikkat edin, isim okuma kısmımızın temelini numara oluşturuyor. Biz numarayı veriyoruz, o da bize isimleri veriyor. Anahtar olay numara. Şimdiki bilgimiz ve programımızla numaralarını hatırlamak suretiyle herkesin adını bulabiliriz. Ya da son numarayı bulabildiğimize göre tüm isimleri bir liste halinde ekrana (PRINT) veya printer'a (LPRINT) aktarabiliriz. Bu pek az insanı mutlu edecektir. Bizim girilen bir ismi aramamız şart. Birazcık burada kafa karıştırayım. Neleri bilip, neler isteyebiliriz? Mesela adamın soyismi aklımızda (Öğretmensek mesela), olur numarasını merak ederiz. Veya ismini biliyoruz numarayı merak ediyoruz. Olur ya, numarayı biliyoruz, numaranın kime ait olduğunu merak ediyoruz. Hatta adamın ismini biliyoruz, soyismini arıyoruz, soytarılığı bırakalım (daha doğrusu ben bırakayım). Kişi hakkındaki bilgilerimiz tam da olmayabilir. Bu kadar sıralamamın sebebi, aramamızı bu başlıkları kapsayacak şekilde yapmak. Gerçi örneğimizde en mantıklısı isimleri karşılaştırmak ancak bunu biraz genişletebiliriz.
Arama:
Input "Aranan ismi girin ";a$
a$ = UCASE$(a$)
if a$ ="" then goto arama_biter
OPEN Son_isim FOR INPUT AS 2
INPUT #2,i
CLOSE #2
For numara = 1 to i
gosub İsim oku
b$ = iisimS+soyisimS
if INSTR (UCASE$(b$),a$) <> 0 then go-
sub isim yaz
next numara
arama biter:
Return
Biraz önce fuzuli gibi gözüken satırların yararını şimdi gördük. İsim yazmak için aynı satırları kullanmamız gerekmedi. Burada dikkat ederseniz sadece isim değil, soyismi girilerek de aranan isim bulunabilir. En güzeli bu iş için arananın niteliğinin belirtilmemesi. UCASE$ fonksiyonu bugün ikinci kez karşımıza çıkıyor. Büyük ve küçük harflerin karışmaması için aramalarınızda bu emri kullanmaya dikkat edin. Sorunlarımız daha tam olarak çözülmedi. 10000 numara arasından bir numara bulabilmek için, tüm numaraları teker teker okumamız gerekiyor. Bu konu üzerinde biraz sonra tartışalım.
Şimdi bir diğer mesele, bir kayıt silmek istiyoruz. Bu konu biraz karışık. Size yardımcı olacak bir iki öneri getirmeye çalışayım. Eğer sondan silmek istiyorsak bu sorun değil. Yapacağımız tek şey Son_isim isimli dosyada bulunan değeri okuyup, düşürmek ve tekrar yazmak. Ancak programlarınız özel şartlar dışında da akıllıca davranabilmeli. Aşağıdaki harf sırasını göz önüne alalım.
A-B-C-D-E-F-G-H-I-J-K-L
Her harf, bir kaydı simgelesin. 12 adet kaydımız var. Ve Son_isim dosyasındaki sayı, yani numara değeri 12. İlk olarak sadece bir kayıt silmek isteyelim. Dosya sonda ise yapılacak tek şey Son_isim dosyasında bulunan sayıyı küçültmek. Bu silinmesi istenen kaydı silmeyecek, ancak yeni kayıt yapıldığı zaman silinmesi istenen kaydın üzerine kayıt yapılacaktır. Ama bizim kayıt sonda değil de, diyelim E harfi ile gösterilen kayıt olsun. Bu durumda yapabileceğimiz en basit ve etkili yol yine Son_isim dosyasında bulunan numarayı küçültmek, ne de olsa bir kayıt silince toplam kayıt sayısı düşecektir. Bunun ardından en son kaydımızı silinmesi istenen kaydın üzerine yazarız. Dizi şu şekli alır:
A-B-C-D-L-F-G-H-I-J-K





Birden fazla kaydı silmek için ise, bu işlemi teker teker uygularız. Bu yöntemin sakıncası, bir dosyayı silmek için üzerine kayıt yapmak zorunda oluşumuz, zaman kaybedişimizdir. Yine de, bence en mantıklısı bu yolu kullanmaktır. Bir ikinci yol ise tüm kayıtlar için paralel bir kayıt tutmaktır. Herhangi bir dosyanın geçerli olup olmadığını bu kayıt üzerinde bulabiliriz. Mesela silinmesi gereken kayıtlara 0, geçerli kayıtlara 0 harici bir sayı veririz. Yeni bir kayıt girerken ilk olarak bu diziyi inceler ve geçersiz kılınan dosyaların üzerine kayıt yaparız. Yok eğer geçersiz kayıt yok ise o zaman klasik sona ekleme yöntemine devam ederiz.
A-B-C-D-E-F-G-H-I-J-K-L 1-1-1-1-0-1-0-1-0-1-1-1
Örnekte E, G, I kayıt dosyaları geçersiz olarak işaretlenmiştir. Yeni bir kayıt yapılınca sıra ile bu boşluklar doldurulacaktır. Sözgelimi M, N, O ve P kayıtları eklenince:
A-B-C-D-M-F-N-H-O-J-K-L-P 1-1-1-1-1-1-1-1-1-1-1-1-1 halini alacaktır. Bu yol diskette bir başka dosya oluşturur. Hafızada fazladan yer işgal eder, üstüne üstlük basitçe hallettiğimiz kayıt durumunu inceleyen program kısmına ek karışıklıklar getirir. Sözü uzatmaya gerek yok. Meraklı olanlar biraz başağrısı ile programlarına bu yolu ekleyebilirler. Önce ilk silme işlemine ilişkin program kısmını yazalım (bu kısmı arama kısmına ilave edebilirsiniz. Bir isim aradığınızda, bulunan isim IF ile sorulmuş ve GOSUB isim_yaz denmişti. Bu bloğa devam ederek ve ENDIF ile bitirmek suretiyle):
isim_sil:
REM silinmesi istenen numara = sil isim
REM Son numara
= numara
if sil isim = numara then goto sonda
get #1,numara
put #1,sil_isim
sonda:
numara = numara -1
OPEN Son_isim FOR INPUT AS 2
PRINT #2,numara
CLOSE #2
return
Belirttiğim gibi, silinmesi istenen numarayı bulabilmek için arama kısmından yararlanabilir, yani arama kısmına seçenek olarak ekleyebilir veya ayrı bir arama kısmı yazabilirsiniz. Artık programımız tamam. Yani her işi yapan bir programımız var. Sıra bunu bir düzene koymakta. İlk olarak sabitlerimizi yerleştirmeliyiz. Şu Son_isim ve D_isim adı verilen değişkenleri tanımlamalıyız. Bu isimlerin mantıklı bir dosya adresi olması gerekiyor. Yoksa File not Found şeklinde hatalar alırsınız. Tanımlamalardan sonra ise sıra bir menüye geliyor. Bunun için ekran temizlenir, programın ismi ve seçenekleri yazılır. Daha sonra ise seçeneklerden birinin seçilmesi beklenir. Bunun ardından gerekli bölümlere dallanılır ve geri gelinir. En son olarak bir programı bitirme seçeneği bırakılır, burada ise devamlı açık tuttuğumuz 1 numaralı dosya CLOSE #1 ile kapatılır.
Tanımlar:
Son isim="Df1:Amiga/dosya.son"
menu:
CLS
Print "Seçenekler"
Print" 1. Yeni_kayıt...."
print" 6. Son"
INPUT, INKEY$, ON MENU v.s.
GOSUB lar
Goto menu
Son:
Close #1
End
Altprogramlar
isim_yaz:
yeni_isim:
Programımız hazır. Şimdi hataları bulmaya başlayalım. Noktalama hataları, estetik hataları, mantık hataları (olur, olur). Böylece beraber bir program yazmış olduk. Anlatmak istediğim, bir yerde yazılı bir programı çalıştırmakla kalmadık, program parçalarını alıp birleştirdik ve harbiden program yazdık. Sizi tebrik ederim (umarım çalışıyordur). Hala bir kaydı silmek için tüm dosyayı taramak zorunda kalıyoruz. Ufak uygulamalarımız olduğu düşünülürse, bu bizi o kadar rahatsız etmemeli. Bu sorunların önüne geçebilmek için, gerekli yöntemler köşemizin amacı dışında kalıyor. Elimizdeki programın verimini arttırmak için, bir iki öneride bulunmak istiyorum: Çalışmadan önce ilgili iki dosyayı RAM:'a veya varsa RAD:'a kopyalayın. Dosyaların erişim zamanı aşırı artacaktır. Ne var ki, çakılmalara ve elektrik kesilmelerine karşı savunmanız düşecektir. Sorunu dosyayı sık sık (yeniden yazmak istemediğiniz kadar yazdıkça) diskete kaydederek çözebilirsiniz. Bu iş için program içinde ek bir dosya açarak veya CLI'nın Copy emirlerini kullanarak yapabilirsiniz. İşiniz bitince dosyanın kapandığından emin olun. Aksi takdirde 0 boyutlu cici bici bir dosyanız olacaktır. Ne yazık ki, elektrik kesilmeden önce size haber vermeyecek ve dosyanız açık kalacaktır. Bunun önüne geçebilmek için, dosyayı tüm program süresince değil de, sadece ihtiyacınız oldukça OPEN ile açın. Ardından hemen CLOSE ile kapatın. En tehlikelisi ise diskete yazı yazılırken elektrik kesilmesidir (elektrik kesilmesine Resetler ve sistemin kötü şakaları da dahil). Bu durumda diskete zarar gelme olasılığı yüksektir ve Disk Structure Corrupt mesajları bile alabiliriz. Tüm bu tehlikelere rağmen yılmayın devam edin (sözü geçen sorunlar 40 yılda bir oluyor).
Programınızın hızı az mı geliyor, daha hızlı bir Basic ile mesela Hisoft ile çalışın, başka dil kullanın (Pascal, C) veya gidin Superbase veya Miamiga File ile oynayın.
Basic ile ilgili sorunlarınıza eğilmeye devam edeceğiz. Sorularınız varsa mektup yazın. Eğer benim yazılarımla ilgili sorunlar veya programlarda hatalar varsa, lütfen adımı kullanın (bulmak daha kolay oluyor!) Gelecek sayıda bazı süsleme tekniklerine değinmek istiyorum (araya başka bir şey girmezse), hepinize sevgiler, başarılar.




