16. Bölüm
Cenker Özkurt
Amiga severlere selam! Yazı dizimize, dosya işlemlerini daha derinlemesine inceleyerek devam ediyoruz. Geçen ay verdiğimiz kopya programı da, dosya işlemleriyle ilgili bir örnek programdı. Bu ay biraz daha fazla açıklama yaparak konumuza devam edeceğiz.
Bildiğiniz gibi, bilgisayarla yaptığınız çalışmalarda verilerin emin bir yere kaydedilmesi gerekir. Bu nedenle dosyalama işlemlerinin sağlıklı yapılması çok önemlidir. Bu konuyu dosyalama işlemlerini buffer'lı input/output ve buffer'sız input/output (I/O) olmak üzere ayrı ayrı inceleyeceğiz.
C dilinde iki değişik UNIX standardı rutin mevcuttur. Bunlardan birincisi 'Buffered file system', ikincisi ise 'Unbuffered file system' dir. Birinci sistemde veriler buffer'dan geçirilerek işlem görürler. Bu sistem ASCII karakterlerden oluşan dosyaların okunup yazılmasında kullanılır.
İkinci sistemde ise buffer kullanılmaz. Bu sisteme binary sistem denir. Yani istenen formatta bilgiyi okuyup yazabilir.
ANSI (American National Standards Institute) standardı, 'Buffered file system'in geliştirilmiş halidir. Şu anda da en çok kullanılan sistem ANSI standardı sistemidir. Çünkü bu sistem hem 'text', hem de 'binary' dosyalama işlemlerini 'Buffered file system' kullanarak gerçekleştirmektedir. Biz de bu yazımıza 'Buffered file system' ile başlayacağız.
İlk olarak klavyeden (konsoldan) yapılan input/output'ları inceleyeceğiz. Klavyeden yapılan I/O'lar Buffered file system'in özel bir kullanım şeklidir. Bilgisayar açıldığında, klavyeyi otomatik olarak bir sıralı dosya sistemi gibi algılamaya başlar. Bu sistemi kullanan komutları daha önceki örneklerimizden de hatırlayacaksınız. Bu komutlar Aztec derleyicinizin içerisindeki <stdio.h> (Standart input/output) içerisinde tanımlanmaktadır. Bu nedenle bu fonksiyonları kullanmadan önce #include <stdio.h> satırını programımızın başına eklemeyi unutmamalıyız.
Stdio.h'ın içerisindeki fonksiyonlar hatırlayacağınız gibi: getchar(), putchar(), gets(), puts() komutlarıdır.
getchar() klavyeden tek bir karakter beklerken, putchar() fonksiyonu bir karakteri ekrana yazmak için kullanılır. getchar() ile girilen her karakter aynı zamanda otomatik olarak ekrana da yazılmaktadır. Örnek olarak aşağıdaki programı inceleyelim;
#include <stdio.h>
main()
{
char c;
printf("Çıkış için ! tuşuna basın.");
do {
c=getchar();
putchar(c);
} while(c !='!');
}
Bu program, '!' tuşuna basılana kadar girdiğimiz her karakteri ekrana yazarak tekrarlayacaktır.
gets() ve puts() fonksiyonları da aynı şekilde çalışmaktadır. Fakat getchar() ve putchar() komutlarının aksine, birden fazla karakter okuyup ekrana yazmak için kullanılırlar. gets() ile bir dizi karakter arka arkaya girilebilir. Dizinin tamamlanması enter tuşuna basmakla sağlanır. Enter tuşuna basıldığında dizinin sonuna boş bir karakter ilave edilerek, dizi tamamlanır, puts() ise bu dizinin tamamını ekrana yazar.
#include <stdio.h>
main()
{
char dizi[30];
gets(dizi);
puts(dizi);
printf("Uzunluk = %d",strlen(dizi));
}
Yukarıdaki program gets() ve puts() için basit bir örnek.
Artık Buffer'lı dosyalama sistemine göz atmaya başlayabiliriz. Buffer'lı dosyalama işlemleri bir ara hafıza kullanmaktadır. Bu işlemleri yapan fonksiyonların tanımlamaları da C kütüphanelerinden <stdio.h>'ta yer almaktadır. Her dosyalama işlemi için programımızın başına '#include <stdio.h>' satırını eklemeyi unutmamalıyız.
Buffered file system'in kullandığı dosya işleme komutları ve fonksiyonlan ise kısaca şöyledir:
İsim Fonksiyon
---- ---------
fopen() Bir adet dosya açar.
fclose() Bir adet dosyayı kapar.
fputc() Dosyaya bir karakter yazar.
fgetc{) Dosyadan bir karakter okur.
fputs() Dosyaya bir dizi yazar.
fgets() Dosyadan bir dizi okur.
fseek() Belirtilen bir byte'ı kütükte arar.
fprintf() Printf()'in yaptığını, fprintf() dosya için yapar.
fscanf() Scanf()'in yaptığını, fscanf() dosya için yapar.
fread() Dosyadan birden fazla bilgi okur.
fwrite() Dosyaya birden fazla veri yazar.
feof() Dosya sonuna geldiğinde true yollar.
ferror() Bir hata oluştuğunda true yollar.
rewind() Dosya pozisyon belirtecini dosyanın başına getirir.
Buffer'lı dosya sisteminde çalışırken bu dosya için <stdio.h>'ın içindeki FILE sistem structure'ı bir pointer olarak tanımlanır. Bu pointer'in içinde dosya ile ilgili bilgiler saklanmaktadır. Şimdi bu komutların çalışmalarını inceleyelim.
fopen() komutu; bir dosyayı okuma, yazma veya ekleme olarak üç ayrı amaçla kullanıma açar. Daha sonra dosya yerini belirlemek için dosya pointer değeri ile ana programa geri döner. Böylece dosya hakkındaki bilgiler pointer'e aktarılmış olur. fopen() fonksiyonunun kullanımı genel olarak şöyledir:
FILE *fp; fp=fopen(dosya adı, modu);Burada mod, ANSI standardına göre dosyanın açılma amacını belirler. Mod'un alabileceği değerler;
Mod Amacı --- ----- "r" ASCII dosyayı okumak için açar. "w" ASCII dosyayı yazmak için oluşturur. "a" ASCII dosyaya ekleme yapmak için açar. "rb" Okumak için binary dosyasını açar. "wb" Yazmak için binary dosyasını açar. "ab" Eklemek için binary dosyasını açar. "r+" Okuma/yazma için ASCII dosyasını açar. "w+" Okuma/yazma için ASCII dosyasını oluşturur. "a+" Okuma/yazma için ASCII dosyasını açar. "rb+" Okuma/yazma için binary dosyasını açar. "wb+" Okuma/yazma için binary dosyasını oluşturur. "ab+" Okuma/yazma için binary dosyasını açar.Burada dikkat ettiyseniz ANSI standartının getirmiş olduğu bir özellik olarak bir dosyayı hem ASCII, hem de binary olarak 'Buffered file system' açabiliyoruz.
fp değişkeni, FILE tipinde olup dosya pointer değişkeni olarak tanımlanır. FILE dosya structure'ı ise, <stdio.h> kütüphanesi içerisinde tanımlanmaktadır, fp dosya pointeri, fopen() ile verilen bir değer dışında değiştirilmemelidir.
Dosya adı, bir karakter dizisi şeklinde verilir. Dosya ismi bir direcıory altında olacak şekilde de tanımlanabilir (paıh belirleme).
Dosya açılmasına bir örnek verirsek:
#include <stdio.h>
main()
{
FILE *fp;
fp = fopen("deneme","r");
if(fp == NULL) {
printf("Dosya bulunamadı");
exit();
}
printf("Dosya bulundu");
fclose(fp);
}
Burada fp dosya pointeri olarak tanımlanıyor. Daha sonra 'deneme' adı altında bir dosya yalnız okuma amacıyla açılıyor. Eğer böyle bir dosya yoksa, 'Dosya bulunamadı' mesajıyla programdan çıkılıyor. Aksi takdirde 'Dosya bulundu' yazılarak açılan dosya kapanıyor ve program sona eriyor.
Buradaki NULL macro değişkeni 0 değerindedir. <stdio.h>'ta tanımlanmaktadır. Eğer fopen() dosyayı bulamazsa, NULL return koduyla geri dönmektedir.
Şimdi bir dosyayı yazma için açalım,
#include <stdio.h>
main()
{ FILE *fp;
fp=fopen("deneme","w");
if(fp==NULL) {
printf("Dosya açılamadı");
exit();
}
fputs("veri",fp);
fclose(fp);
}
Eğer dosya açılırsa, "veri" datası deneme dosyasına yazılıyor. Buradaki fputs() fonksiyonunun kullanımına dikkat edin. Normal puts() fonksiyonundan farklı olarak, bir de yazılacak olan dosya pointer'ını kullanıyor. Aynı şekilde, fgets() fonksiyonu da alacağı veriyi dosya'dan, dosya pointer'ı sayesinde alıyor. Kullanımlarına dikkat etiyseniz, fgets() ve fputs() komutlarının kullanımı oldukça kolay. Tek fark dosya pointer'ına bağımlı olmaları.
Şimdi, fseek() fonksiyonuna gelelim. Bu fonksiyon dosya pozisyon göstergecini istenen pozisyona getirir. Bu komut sayesinde buffer'lı input/output sisteminde random olarak dosyadan okuma/yazma işlemleri yapılabilir. Genel kullanım şekli ise şöyledir;
fseek(fp,offset,origin);fp, file pointer'ı yani dosyanın hafızada bulunduğu yer. 'offset' ise origin'den itibaren olan byte sayısı. Diğer bir deyişle, eğer biz origin'i başlangıç olarak kabul edersek, offset'i ise 10 verirsek, fseek komutu dosya pointer göstergecini dosyanın başlangıcından itibaren 10 byte ileri alacak şekilde bize hazırlayacaktır. Bu da bizini bir dosyaya random olarak ulaşmamızı sağlayacaktır. Burada 'origin' üç şekilde olabilir;
değer origin ----- ------ 0 Dosya başı 1 Aktif pozisyon 2 Dosya sonufseek() komutunun işlevi bu kadar. Sıra fprintf() fonksiyonuna geldi.
fprintf() fonksiyonu, printf() fonksiyonunun ekran için yaptığım dosya için yapar. Burada fprintf(). printf()'ten farklı olarak bir de dosya pointer değişkenini istemektedir. Genel kullanım şekli ise şöyledir;
fprintf(fp,"kontrol dizisi",argument listesi);Kontrol dizisi ve argument listesi printf() fonksiyonunda olduğu gibi işler. Bir örnek verelim:
#include <stdio.h>
main()
{
char c;
int a,b;
FILE *fp;
fp=fopen("deneme","w");
if(fp == NULL) {
printf("Dosya açılamadı");
exit();
}
fscanf(stdin,"%d %d %c",&a,&b,&c);
fprintf(fp,"%d %d %c",a,b,c);
fclose(fp);
}
fprintf() fonksiyonu, verilen değişkenleri dosyaya yazıyor. Bir de dikkat ettiyseniz fscanf() fonksiyonunu kullandık. Fakat burada bilgiyi bir dosyadan değil de klavyeden aldık. Dosya için stdin yerine fp kullanmalıyız. fscanf() fonksiyonun genel kullanım şekli fscanf()'e benziyor.
fread(buffer,byte sayısı,adet,fp); fwrite(buffer,byte sayısı,adet,fp);Burada buffer, veri için kullandığımız değişkeni veya alanı belirtiyor. Byte sayısı ise okunacak veya yazılacak byte'ı, adet ise bu işlemin sayısını belirtiyor. Aşağıdaki örneği inceleyelim:
#include <stdio.h>
main()
{
char s[50];
FILE *fp;
strcpy(s,"Deneme datası");
fp = fopen("deneme","w");
if(fp==NULL) {
printf("Dosya açılamadı");
exit();
}
fwrite(s,strlen(s),1,fp);
fclose(fp);
}
'Deneme' datası, deneme dosyasına 1 kez olmak üzere yazılacaktır. Adedi 2 verirsek. bu işlemi iki defa yapmış oluyoruz. Yani "Deneme datası" arka arkaya iki defa dosyaya aktarıyor.
feof() fonksiyonu, dosya sonuna gelip gelmediğimizi kontrol eder. Sıfırdan farklı bir sayı, dosya sonuna geldiğimizi gösterir. Kullanımı:
feof(fp);şeklindedir.
ferror(), bize varsa, dosya hatasını bildirir. Yolladığı değer pozitif ise hata olduğunu, negatif ise hata olmadığını belirtir.
ferror(fp);şeklinde kullanılır.
rewind() komutu, dosya pozisyon göstergecini başa getirecek şekilde hazırlar.
Kullanımı:
rewind(fp):şeklindedir.
Bu aylık buraya kadar. Gelecek ay, dosya işlemlerinin ikinci kısmı olan 'Unbuffered file system'i inceleyeceğiz. Böylece C'de oldukça önemli olan dosya işlemlerini de öğrendikten sonra, artık daha yeni programlara adım alabiliriz.
Gelecek ay yeni bir C yazısında buluşmak üzere hepinize iyi çalışmalar....