C Programlama

Ek Bilgiler

C'de Dosya İşlemleri

► Detaylı anlatım

Bu bölümde C dilinin dosya giriş ve çıkış işlemlerini incelemeye çalışacağız. Bu işlemler üzerinde çalışmaya başlamadan önce, akış ve dosya kavramlarını ayrı ayrı ele alalım:

Akış

C'de, dosya giriş çıkış işlemlerinin temelini, bilgisayarı oluşturan aygıtlar için mantıksal bir arabirim görevi yapan, Akış adı verilen bir kavram oluşturur. Genel bir ifade ile, akış bir dosyaya ulaşmak için kullanabileceğiniz mantıksal bir arabirimdir.

Akış, stdio.h başlık dosyası içinde tanımlı FILE adlı yapı veri türünden bildirimi yapılan bir işaretçi değişkeni ile kullanılan bir dosya veya fiziksel bir cihazdır.

Bir akışın dosya (fiziksel aygıtlar dahil) ile bağlantısını sağlamak için açma işlemi, dosyadan bağlantısını kesmek için kapama işlemi uygulanır. Dosya açıldıktan sonra, dosyadan programınıza bilgi aktarabilir veya dosya içeriğini değiştirebilirsiniz.

2 tür akış vardır :

1. Metin akışı
2. İkili sistem akışı

Metin Akışı

Metin akışı, herhangi bir metin editörü ile oluşturulan DOS metin dosyaları ile kullanılır. Yani, metin akışı ASCII karakterlerle birlikte kullanılır. C dilinde metin akışı, metin dosyalarını satırları tek bir yeni satır karakteri (ASCII 10) ile ayrılmış olarak dikkate alır. Ancak, DOS metin dosyaları, her satırı arasında bir yeni satır (ASCII 10) ve bir satır başı (ASCII 13) karakteri olmak üzere toplam iki karakter ile, diske kaydedilir. C dili, metin akışına diskten bir dosya okurken yeni satır ve satır başı karakterlerini tek bir yeni satır karakterine çevirir. Metin akışından diske bir dosyayı kaydederken ise, tek bir yeni satır karakterini, yeni satır ve satır başı karakterlerine çevirir. Bu nedenle, metin akışına gönderilen ifadelerle dosyaya yazılan ifadeler tıpatıp aynı olmayabilir.

İkili Sistem Akışı

İkili sistem akışında ise, herhangi bir karakter dönüşümü yapılmaz. Yani, ikili akıştan diske bir dosya kaydederken veya diskten ikili akışa bir dosya aktarırken, dosya içinde yer alan karakterlerde herhangi bir değişiklik yapılmaz. ikili sistem akışına gönderilen ifadelerle dosyaya gönderilen ifadeler tamamen birbirinin aynıdır.

Aktif Konum

Aktif konum, bir dosyaya yapılacak bir sonraki girişin yapılacağı konumdur. Örneğin, 80 byte uzunluğunda bir dosyanın ilk erişimde 20 byte ının okunduğunu kabul edelim. Bir sonraki dosya okuma işlemi 21 nci byte dan başlayacaktır. Bu durumda, 21 nci byte aktif konum olarak kabul edilir. Başka bir ifade ile, dosya işlemlerinde yapılan bir sonraki işlem dosya başından değil kalınan yerden devam eder.

Dosya Sistemi

C dosya sisteminde kullanılan dosya kavramının, normal bir disk dosyası için kullanılabileceği gibi, ekran, klavye gibi bilgisayar parçaları içinde kullanılabileceğinden bahsetmiştik.

Bir dosya açıldığı zaman, aktif konum göstergesi dosya başlangıcını gösterir. Dosyadan her okuma ve yazma işleminde, aktif konum göstergesi otomatik olarak bir sonraki konuma geçer.

Standart Akışlar

Bir programı çalıştırdığınız zaman, program aşağıda gösterilen standart metin akışlarını otomatik olarak açar. Bu metin akışları yoluyla, program bilgisayara bağlı aygıtlara (Klavye, Ekran, Yazıcı gibi...) tıpkı bir dosya gibi erişebilir:

stdin  : Klavyeden veri okur.

stdout : Verileri ekrana yollar.
stderr : Verileri ekrana yollar.

stdaux : Seri port'tan veri alışverişi yapar.

stdprn : Verileri yazıcıya yollar.
#include <stdio.h>

void main (void)
{
  char cdizi[40];

  printf("Bilgisayar");
}

Yukarıdaki örnekte, program normal olarak çalıştığında ekrana aşağıdaki kelimeyi yazar:

Bilgisayar

Yukarıdaki programın derlenmiş adının deneme.exe olduğunu kabul edelim. Bu durumda komut satırında aşağıdaki satırı yazarsanız, stdout deneme.txt adlı dosyaya yönlendirildiğinden, program Bilgisayar kelimesini deneme.txt adlı dosyaya yazar:

deneme > deneme.txt

Temel dosya işlem fonksiyonları aşağıda yer almaktadır:

Fonksiyon   İşlevi

fopen( )           Bir dosya açar.
fclose( )          Bir dosyayı kapatır.
fputc( ) ve putc() Dosyaya bir karakter yazar.
fgetc( ) ve getc() Dosyadan bir karakter okur.
fseek( )           Bir dosyadaki belirli bir byte'ı bulur.
fprintf( )         Yapılandırılmış verileri dosyaya yazar.
fscanf( )          Yapılandırılmış verileri dosyadan okur.
feof( )            Dosya sonu geldiğinde doğru bir değer verir.
ferror( )          Bir hata durumunda doğru bir değer verir.
rewind( )          Dosya aktif konumunu başa alır.
remove( )          Dosyayı siler.
fflush( )          Tampon belleği siler.

Dosya Açma

Şimdi, dosya açma ve kapama işlemlerinin yapılmasını, dosyadan veri okunması ve dosyaya veri yazılması işlemlerini incelemeye çalışacağız.

Bir dosyayı açmak ve bu dosyayı ilgili akışla birleştirmek için aşağıda prototipi verilen fopen() fonksiyonunu kullanmanız gerekir:

FILE *fopen (char *dosya-adı, char *mod);

dosya-adı: açılacak dosya adı
mod      : dosya açılış şekli

fopen() fonksiyonu stdio.h başlık dosyasını kullanır. dosya-adı ifadesi ile gösterilen ve fopen() fonksiyonuna argüman olarak geçirilen dosya adı işletim sisteminde kullanılan geçerli bir dosya adı olmalıdır. fopen() fonksiyonuna argüman olarak geçirilen ve mod ile gösterilen karakter dizisi ise dosyanın ne şekilde açılacağını belirler. Burada, mod ifadesi yerine kullanılabilecek değerler aşağıdaki tabloda yer almaktadır:

MOD   Anlamı

r     Okuma için bir metin dosyası açar.
w     Yazma için bir metin dosyası oluşturur.
a     Bir metin dosyasına ekleme yapar.

rb    Okuma için bir dosyayı ikili sistemde açar.
wb    Yazma için ikili sistemde bir dosya oluşturur.
ab    İkili sistemde bir dosyaya ekleme yapar.

r+    Okuma ve yazma için bir metin dosyası açar.
w+    Okuma ve yazma için bir metin dosyası oluşturur.
a+    Okuma ve yazma için bir metin dosyası oluşturur veya ekleme yapar.

r+b   Okuma ve yazma için bir ikili sistem dosyası açar.
w+b   Okuma ve yazma için bir ikili sistem dosyası oluşturur.
a+b   Okuma ve yazma için bir ikili sistem dosyasına ekleme yapar.

Eğer dosya açma işlemi başarılı olarak sonuçlanırsa, fopen() fonksiyonu geçerli bir dosya işaretçisi geri verir. FILE, stdio.h dosyası içinde tanımlanmış olup dosyanın boyutu, aktif konumu ve giriş modları gibi dosya hakkındaki değişik değerleri içeren bir YAPI'dır. Daha ilerideki bölümlerde göreceğimiz gibi, yapı tek bir isimle giriş yapılan bir grup değişkenden oluşur. fopen() fonksiyonu dosya açma işlemi ile birlikte dosya ile ilgili yapıya bir işaretçi geri verir. Bu işaretçi diğer fonksiyonlarla dosya üzerinde işlem yapmak için kullanılır.

Eğer fopen() fonksiyonu normal olarak çalışmazsa NULL bir işaretçi geri verir. NULL stdio.h dosyasında null bir işaretçi olarak tanımlanmıştır. fopen() fonksiyonunun geçerli bir dosya işaretçisi geri vermesi gerekir. Geri verilen değerin NULL olmadığından emin olmak için geri verilen değeri kontrol etmek gerekir.

Dosya Kapatma

Bir dosyayı kapatmak için aşağıda prototipi verilen fclose() fonksiyonu kullanılır :

int fclose (FILE *fp);

fp: fopen() ile kullanılan dosya işaretçisi

fclose() fonksiyonu fp ile bağlantılı dosyayı kapatır ve dosyanın akış ile olan bağlantısını keser. fclose() fonksiyonu normal olarak çalışırsa 0, bir hata meydana gelirse EOF değerini geri verir.

fgetc() ve fputc() Fonksiyonları

Bir dosya açıldıktan sonra, açılış moduna bağlı olarak, aşağıda genel yapısı verilen 2 fonksiyonu kullanarak dosyaya bir karakter yazabilir veya dosyadan bir karakter okuyabilirsiniz:

int fgetc (FILE *fp);
int fputc (int id, FILE *fp);

fgetc() fonksiyonu, fp ile tanımlanan dosyadaki bir sonraki byte'ı unsigned char olarak okur ve int bir değer olarak geri verir. fgetc() fonksiyonu bir hata durumunda ve dosya sonuna geldiğinde int bir değer olan EOF değerini geri verdiği için, geri verilen değerin kontrolü amacıyla fgetc() fonksiyonu int bir değer geri verir. Ancak, fgetc() fonksiyonunun geri verdiği değeri int bir değişkene atamanız şart değildir. Geri verilen değeri bir karakter değişkene de atayabilirsiniz.

fputc() fonksiyonu d1 değişken değeri olan byte'ı fp ile gösterilen dosyaya unsigned char olarak yazar. id değişkeni int bir değer olarak tanımlandığı halde, char bir değer olarak çağırabilirsiniz. Eğer dosyaya yazma işlemi başarılı olursa, fputc() fonksiyonu yazılan karakteri aksi takdirde EOF değerini geri verir.

fgetc() yerine getc() ifadesini, fputc() fonksiyonu yerine ise putc() ifadesini kullanabilirsiniz. Sonuçta, fonksiyonlar tarafından yapılan işlem tamamen birbirinin aynıdır.

#include <stdio.h>

void main (void)
{
  FILE *fp1;
  int id1;

  if ((fp1 = fopen ("dosya1.txt", "w")) == NULL) {
      printf("Dosya açma hatası!");
      exit(1);
  }

  for (id1=0; id1<10; id1++) fputc ('a', fp1);
  fclose(fp1);

  if ((fp1 = fopen ("dosya1.txt", "r")) == NULL) {
      printf("Dosya açma hatası!");
      exit(1);
  }

  for (id1=0; id1<10; id1++) printf("%c", fgetc(fp1));
  
  fclose(fp1);
}

Yukarıdaki örnekte, program aşağıdaki satırı ekrana yazar:

aaaaaaaaaa

Program, fputc() fonksiyonunu kullanarak, her defasında bir karakter olmak üzere, 10 adet 'a' harfini (w) modunda açılan dosyaya yazar. Bu işlemi bitirdikten sonra, dosyayı kapatır. Dosyayı (r) modunda açtıktan sonra, her karakteri birer birer dosyadan okuyarak ekrana yazar.

feof() ve ferror() Fonksiyonları

fgetc() fonksiyonu, aşağıda belirtilen 2 farklı durumda, EOF değerini geri verir:

1. Bir hata meydana geldiğinde
2. Dosya sonuna gelindiğinde

fgetc() fonksiyonu EOF değerini geri verdiğinde, yukarıdaki durumlardan hangisine göre bu değeri geri verdiğini bilemezsiniz. EOF değerinin fgetc() fonksiyonu tarafından hangi nedenle geri verildiğini anlayabilmek amacıyla aşağıda prototipleri verilen feof() ve ferror() fonksiyonlarını programlar içinde kullanabilirsiniz:

int feof (FILE *fp);
int ferror (FILE *fp);

Eğer fp ile gösterilen dosyanın sonuna gelinmişse, feof() fonksiyonu 0 olmayan bir değer, aksi takdirde 0 değerini geri verir. feof() fonksiyonunu hem ikili sistem hem de metin dosyaları ile birlikte kullanabilirsiniz.

ferror() fonksiyonu fp ile gösterilen dosyada bir hata bulursa 0 olmayan bir değer, aksi takdirde 0 değerini geri verir.

ferror() fonksiyonu sadece yaptığınız en son dosya girişinden sonraki hata kontrolünü sağladığı için, her dosya işleminden sonra bu fonksiyonu tekrar çağırmanız gerekir.

Şimdi, feof() ve ferror() fonksiyonlarının kullanılmasını örnekler üzerinde incelemeye çalışalım:

#include <stdio.h>
#include <stdlib.h>

void main (int argc, char *argv[])
{
  FILE *fp1, *fp2;
  char cd1;

  if (argc!=3) {
      printf("Kullanım: deneme <Kaynak dosya> <Hedef dosya>\n");
      exit(1);
  }

  if ((fp1=fopen(argv[1], "rb")) == NULL) {
      printf("Kaynak dosya açılamadı!\n");
      exit(1);
  }
  if ((fp2=fopen(argv[2], "wb")) == NULL) {
      printf("Hedef dosya açılamadı!\n");
      exit(1);
  }

  while (!feof(fp1)) {
     cd1 = fgetc(fp1);
     if (ferror(fp1)) {
         printf("Kaynak dosyadan okuma hatası!\n");
         exit(1);
     }
     if (!feof(fp1)) fputc (cd1, fp2);
     if (ferror(fp2)) {
         printf("Hedef dosyaya yazma hatası!\n");
         exit(1);
     }
  }

  fclose (fp1);
  fclose (fp2);
}

Yukarıdaki örnekte, program komut satırından argüman olarak verilen kaynak dosyanın içeriğini yine argüman olarak verilen hedef dosyaya yazar. Hedef dosya mevcut değilse oluşturur, mevcut ise önceki içeriğini tamamen siler. Program içinde tam bir hata kontrolü sağlanır.

Eğer programı argümanlarla çalıştırma konusunda sorun yaşıyorsanız, C'de komut istemi kullanımı bölümünü inceleyebilirsiniz.

fputs(), fgets(), fprintf() ve fscanf() Fonksiyonları

C dilinde, metin dosyaları ile çalışırken aşağıda prototipleri verilen 4 fonksiyonla daha pratik işlemler yapabilirsiniz:

int fputs (char *cdizi, FILE *fp);
char *fgets (char *cdizi, int id1, FILE *fp);

int fprintf (FILE *fp, char *kontrol dizisi,...);
int fscanf (FILE *fp, char *kontrol dizisi,...);

fputs() fonksiyonu, cdizi ifadesi ile gösterilen karakter dizisini fp ile gösterilen dosyaya yazar. Normal olarak çalışırsa 0 olmayan bir değer, aksi takdirde EOF değerini geri verir. cdizi karakter dizisini sona erdiren NULL değeri dosyaya yazmaz.

fgets() fonksiyonu, fp ile gösterilen dosyadan okuduğu karakterleri cdizi ifadesi ile gösterilen karakter dizisine atar. Normal olarak çalışırsa cdizi karakter dizisini, aksi takdirde NULL bir işaretçi geri verir. Fonksiyon prototipi içinde verilen id1 değişkeni ise, dosyadan okunacak karakter sayısını belirler. fgets() fonksiyonu, id1-1 kadar karakter okuduğu veya bir yeni satır karakteri ile karşılaştığı zaman, okuma işlemine son verir.

fprintf() fonksiyonu tıpkı printf() fonksiyonu gibi çalışır. Tek fark fprintf() fonksiyonunun ekran yerine dosyalarla çalışmasıdır. Normal olarak çalıştığında dosyaya yazdığı byte sayısını, hata durumunda ise EOF değerini geri verir.

fscanf() fonksiyonu ise tıpkı scanf() fonksiyonu gibi çalışır. Tek fark fscanf() fonksiyonunun ekran yerine dosyalarla çalışmasıdır. Normal olarak çalıştığında, okuduğu ve değişkenlere yüklediği alan sayısını geri verir. Geri verdiği değer değişkenlere yüklenmeyen alanları içermez. Eğer fscanf() fonksiyonu dosya sonunu okursa EOF değerini, hiç bir alan yüklemezse 0 değerini geri verir.

Şimdi, bu dört fonksiyonun kullanılmasını örnekler üzerinde incelemeye çalışalım:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void main (int argc, char *argv[])
{
  FILE *fp1;
  char cdizi[80];

  if (argc!=2) {
      printf("Kullanım: deneme <Dosya adı>\n");
      exit(1);
  }

  if ((fp1=fopen(argv[1], "w")) == NULL) {
      printf("Dosya açılamadı!\n");
      exit(1);
  }

  do {
     printf("Bir karakter dizisi giriniz: ");
     gets(cdizi);
     strcat(cdizi, "\n");
     if (*cdizi!='\n') fputs(cdizi, fp1);
  } while (*cdizi!='\n');

  fclose (fp1);
  if ((fp1=fopen(argv[1], "r")) == NULL) {
      printf("Dosya açılamadı!\n");
      exit(1);
  }

  do {
     if (fgets(cdizi, 79, fp1)) printf(cdizi);
  } while (!feof(fp1));

  fclose (fp1);
}

Yukarıdaki örnekte, program fputs() ve fgets() fonksiyonlarının çalışma şeklini göstermektedir. Program, klavyeden girdiğiniz karakter dizilerini, komut satırından argüman olarak verilen dosyaya yazar. Bir karakter dizisi girmeniz istediğinde ENTER tuşuna basarsanız, dosyaya yazma işlemi sona erer ve dosya kapanır. Daha sonra, program dosyayı tekrar açar. Girdiğiniz karakter dizilerini sıra ile okuyarak ekrana yazar.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void main (int argc, char *argv[])
{
  FILE *fp1;
  char cdizi[40];
  double dd1;
  int id1;
  char cd1;

  if (argc!=2) {
      printf("Kullanım: deneme <Dosya adı>\n");
      exit(1);
  }

  if ((fp1=fopen(argv[1], "w")) == NULL) {
      printf("Dosya açılamadı!\n");
      exit(1);
  }

  fprintf (fp1,"%lf %d %s %c", 654.123, 9852, "Bilgisayar", 'A');
  fclose (fp1);

  if ((fp1=fopen (argv[1], "r")) == NULL) {
      printf("Dosya açılamadı!\n");
      exit(1);
  }

  fscanf (fp1,"%lf%d%s %c", &dd1, &id1, cdizi, &cd1);
  printf("%lf %d %s %c", dd1, id1, cdizi, cd1);
  fclose (fp1);
}

Yukarıdaki örnekte, program aşağıdaki satırı ekrana yazar:

654.123000 9852 Bilgisayar A

Program, fprintf() ve fscanf() fonksiyonlarının çalışmasını gösterir. Program, komut satırından verilen bir dosyaya bir int, bir double, bir karakter dizisi ve bir char olmak üzere toplam dört farklı veri yazar ve dosyayı kapatır. Sonra, dosyayı tekrar açar ve dosyadan okuduğu verileri ekrana yazar.

fread() ve fwrite() Fonksiyonları

Verileri dosyaya yazma ve dosyadan okuma işlemlerinde aşağıda genel yapıları verilen fread() ve fwrite() fonksiyonlarını da kullanabilirsiniz:

size_t fread (void *tampon-bellek, size_t boyut, size_t id1, FILE *fp);
size_t fwrite (void *tampon-bellek, size_t boyut, size_t id1, FILE *fp);

Daha önce kullandığımız fprintf() ve fscanf() fonksiyonları dosyadan veri okuma ve dosyaya veri yazma işlemlerinde veriler üzerinde değişim yaparak çalıştığından, fread() ve fwrite() fonksiyonlarını kullanmak daha pratiktir. fprintf() fonksiyonunu kullanarak bir dosyaya sayı yazarken, sayının dosyanın ASCII metnine çevrilmesi gerekir. Yine fscanf() fonksiyonu ile bir dosyadan bir sayı okurken, sayının fscanf() fonksiyonunun dahili format yapısına çevrilmesi gerekir.

fread() fonksiyonu fp ile gösterilen dosyadan okunan boyut yapısında id1 kadar değeri tampon-bellek ile gösterilen belleğe atar. Burada, boyut ifadesi okunan verinin byte olarak değerini, id1 ifadesi ise kaç adet veri okunduğunu belirler. fread() fonksiyonu okunan veri sayısını geri verir. Bu değer 0 ise, herhangi bir veri okunmamış demektir. Bu durumda, ya bir hata olmuştur ya da dosya sonu gelmiştir.

fwrite() fonksiyonu, fread() fonksiyonunun yaptığı işlemin tam tersini yapar. fwrite() fonksiyonu, tampon-bellek ile gösterilen bellekte bulunan boyut yapısında id1 kadar değeri fp ile gösterilen dosyaya yazar. Burada, boyut ifadesi yazılan byte olarak değerini, id1 ifadesi ise kaç adet veri yazıldığını belirler. fwrite() fonksiyonu yazılan veri sayısını geri verir. Sadece bir hata meydana geldiğinde bu değer id1 değerinden az olur.

fread() ve fwrite() fonksiyonları ile birlikte void işaretçiler kullanılmaktadır. Bu işaretçilerin her tür veriyi göstermesi mümkündür. void işaretçileri fread() ve fwrite() fonksiyonlarında olduğu gibi herhangi bir veri türü ile birlikte kullanabilirsiniz. Bu durumda, fread() ve fwrite() fonksiyonları her türlü veriye işlem yapabilir.

fread() ve fwrite() fonksiyonları ile birlikte kullanılan size_t ifadesi stdio.h başlık dosyası içinde tanımlanmıştır. Bu veri türünden bir değişken derleyicinin desteklediği azami değerin boyutuna eşit bir değer içerecek şekilde ANSI standartları tarafından tanımlanmıştır. Bu durumda, size_t ifadesini unsigned ve unsigned long ifadelerine benzetebilirsiniz. Bu ifadenin kullanılmasının temel nedeni derleyicilerin her türlü çalışma ortamına uyumlu olarak çalışmasını sağlamaktır.

Şimdi öğrendiklerimizi örnekler üzerinde incelemeye çalışalım:

#include <stdio.h>
#include <stdlib.h>

void main (void)
{
  FILE *fp;
  int id1;

  if ((fp=fopen("deneme.txt", "w")) == NULL) {
      printf("Dosya açılamadı!\n");
      exit(1);
  }

  printf("Bir int değer giriniz: ");
  scanf("%d", &id1);

  if (fwrite(&id1, sizeof(int), 1, fp) != 1) {
      printf("Yazma hatası!\n");
      exit(1);
  }

  fclose(fp);

  if ((fp=fopen("deneme.txt", "r")) == NULL) {
      printf("Dosya açılamadı!\n");
      exit(1);
  }

  if (fread(&id1, sizeof(int), 1, fp) != 1) {
      printf("Okuma hatası!\n");
      exit(1);
  }

  printf("Dosyadan okunan değişken değeri: %d", id1);
  fclose(fp);
}

Yukarıdaki örnekteki program fread() ve fwrite() fonksiyonlarının çalışmasını göstermektedir. Program, klavyeden bir int değer girmenizi ister. Girdiğiniz değeri id1 değişkenine atar. Sonra, fwrite() fonksiyonunu kullanarak id1 değişken değerini deneme.txt dosyasına yazar. Daha sonra, fread() fonksiyonu ile aynı değeri dosyadan okutarak ekrana yazar.

#include <stdio.h>
#include <stdlib.h>

void main (void)
{
  FILE *fp;
  int idizi[5];
  int id1;

  for (id1=0; id1<5; id1++) {
       printf("int bir değer giriniz: ");
       scanf("%d", &idizi[id1]);
  }

  if ((fp=fopen ("deneme.txt", "wb")) == NULL) {
      printf("Dosya açılamadı!\n");
      exit(1);
  }

  if (fwrite(idizi, sizeof idizi, 1, fp) != 1) {
      printf("Yazma hatası!\n");
      exit(1);
  }       
  
  fclose(fp);
  
  if ((fp=fopen("deneme.txt", "rb")) == NULL) {
      printf("Dosya açılamadı!\n");
      exit(1);
  }

  printf("\nDeğerleri silinmiş dizi elemanları:\n");
  for (id1=0; id1<5; id1++) {
       idizi[id1] = 0;
       printf("%d ", idizi[id1]);
  }
  printf("\n");

  if (fread(idizi, sizeof idizi, 1, fp) != 1) {
      printf("Okuma hatası!\n");
      exit(1);
  }
  fclose(fp);
  
  printf("Dosyadan okunan dizi değerleri:\n");
  for (id1=0; id1<5; id1++) printf("%d ", idizi[id1]);
}

Yukarıdaki örnekteki programın bir önceki programdan tek farkı dizideki verileri dosyaya yazma ve dosyadan okuma işlemlerini bir adımda gerçekleştirmiş olmasıdır.

Rastgele Erişim

Şimdiye kadar incelediğimiz bütün örneklerde, dosyadan yaptığımız okuma işlemlerini dosyanın başından sonuna doğru bir sıra dahilinde yaptık. Bunun yanında, aşağıda prototipi verilen fseek() fonksiyonunu kullanarak bir dosyanın herhangi bir yerindeki bir bilgiyi okuyabilirsiniz:

int fseek (FILE *fp, long ara, int yer);

Burada, fp ifadesi işlem yapılan dosyayı, ara ifadesi yer ifadesinin gösterdiği değerin tanımladığı dosya konumundan,işlem yapılmak istenen yerin byte olarak uzaklığını verir. yer ifadesi dosyada arama işleminin başlayacağı yeri gösterir. yer ifadesinin yerine aşağıdaki makrolardan birini kullanmanız gerekir:

SEEK_SET (0) Aramayı dosya başından başlatır.
SEEK_CUR (1) Aramayı aktif konumdan başlatır.
SEEK_END (2) Aramayı dosya sonundan başlatır.

Yukarıda gösterilen makrolar stdio.h başlık dosyasında tanımlanmıştır.

fseek() fonksiyonu normal olarak çalıştığında 0 değerini, aksi takdirde 0 olmayan bir değer geri verir.

Ayrıca, aşağıda prototipi verilen ftell() fonksiyonunu kullanarak bir dosyanın aktif konumunu belirleyebilirsiniz:

long ftell (FILE *p);

Normal olarak çalıştığında, fp ile gösterilen dosyanın aktif konumunu, aksi takdirde -1L değerini geri verir.

Rastgele erişim genellikle ikili sistem dosyalarında kullanılır. Metin dosyalarında bu özelliğin kullanılmamasının nedeni, bu dosyalarda karakter değişimlerinin yer almasıdır. fseek() fonksiyonunu metin dosyaları ile kullanmak için daha önce ftell() fonksiyonu ile dosyanın aktif konumunu belirlemeniz ve fseek() fonksiyonunu SEEK_SET ile birlikte tanımlamanız gerekir.

Rastgele erişim metodunu ikili sistem dosyası olarak açılan metin dosyalarına uygulayabilirsiniz, ancak metin dosyası olarak açılan dosyalara uygulayamazsınız.

Şimdi öğrendiklerimizi örnekler üzerinde incelemeye çalışalım:

#include <stdio.h>
#include <stdlib.h>

void main (int argc, char *argv[])
{
  long int lid1, lid2;
  int id1;
  FILE *fp1;

  if (argc!=2) {
      printf("Kullanım : deneme <Dosya adı>\n");
      exit(1);
  }

  if ((fp1=fopen(argv[1], "rb")) == NULL) {
      printf("Dosya açılamadı!\n");
      exit(1);
  }

  /* Dosya sonuna ulaşıp dosya boyutunu kaydeder. */
  fseek (fp1, 0L, SEEK_END); 
  lid2 = ftell(fp1);
  
  for ( ; ; ) {
       printf("Byte sıra no.sunu giriniz: ");
       scanf("%ld", &lid1);
       if (lid1>=lid2) break;
       if (fseek(fp1, lid1, SEEK_SET)) {
           printf("Arama hatası!");
           exit(1);
       }
       id1 = getc(fp1);
       printf("%ld konumundaki değer ASCII %d : %c\n", lid1, id1, id1);
  }

  fclose(fp1);
}

Yukarıdaki örnekte, program komut satırından argüman olarak girdiğiniz dosyayı açar. Sonra, girdiğiniz byte'da yer alan karakteri ve ASCII kodunu ekrana yazar. Dosya boyutundan fazla bir değer girerseniz program sona erer.

remove(), rewind() ve fflush() Fonksiyonları

Aşağıda prototipi verilen remove() fonksiyonunu bir dosyayı silmek için kullanabilirsiniz:

int remove (char *dosya-adı);

remove() fonksiyonu dosya-adı ifadesi ile gösterilen dosyayı siler. Başarılı bir şekilde sona erdiğinde 0 değerini, aksi takdirde 0 olmayan bir değer geri verir.

Bir dosyanın aktif konumunu dosya başına almak için aşağıda prototipi verilen rewind() fonksiyonunu kullanabilirsiniz:

void rewind (FILE *fp);

fp ifadesi ile gösterilen dosyanın aktif konum göstergesini dosyanın başına alır. Bu fonksiyon herhangi bir değer geri vermez. Çünkü, başarılı bir şekilde açılan dosyanın aktif konumu başa alınabilir.

C dilinde, bir dosya ile ilgili tampon belleği boşaltmak için aşağıda prototipi verilen fflush() fonksiyonunu kullanabilirsiniz:

int fflush (FILE *fp);

fflush() fonksiyonu, fp ifadesi ile gösterilen dosyanın tampon belleğini boşaltır. Fonksiyon başarılı olduğu zaman 0 değerini, aksi takdirde EOF değerini geri verir. Eğer fflush() fonksiyonunu argüman olmadan kullanırsanız, mevcut bütün disk tampon bellekleri silinir.

Şimdi, öğrendiklerimizi örnekler üzerinde incelemeye çalışalım:

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <ctype.h>

void main (int argc, char *argv[])
{
  if (argc != 2) {
      printf("Kullanım : deneme <Dosya adı>\n");
      exit(1);
  }

  printf("Dosya silinecek! Devam edecek misiniz (E/H)?");
  if (toupper(getche())=='E') remove(argv[1]);
}

Yukarıdaki örnekte, program komut satırından argüman olarak verdiğiniz dosyayı siler.

#include <stdio.h>
#include <stdlib.h>

void main (int argc, char *argv[])
{
  FILE *fp1;

  if (argc!=2) {
      printf("Kullanım : deneme <Dosya adı>\n");
      exit(1);
  }
  if ((fp1=fopen(argv[1], "r")) == NULL) {
      printf("Dosya açılamadı!\n");
      exit(1);
  }

  while (!feof(fp1)) putchar(getc(fp1));
  rewind(fp1);
  printf("\n");
  while (!feof(fp1)) putchar(getc(fp1));

  fclose(fp1);
}

Yukarıdaki örnekte, program komut satırından argüman olarak verilen dosyayı ekrana yazar. Aynı dosyanın aktif konumunu rewind() fonksiyonu ile başa aldıktan sonra dosya içeriğini bir kez daha ekrana yazar.