Bu bölümde, C temelli 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:
Dosya sistemi farklı donanım elemanları ile çalışma olanağı sağlar. Bu donanım elemanı bir klavye, ekran, disk sürücü veya bilgisayarda kullanılan bir port olabilir.
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 ara birimdir. Ancak burada kullanılan dosya ifadesi, diske kaydedilebilen bir dosyayı ifade etmekle birlikte, ekran ve klavye gibi donanım elemanlarından birini de ifade edebilir. Dosyaların yapıları veya kapasiteleri farklı olabilir, ancak akışların tamamı aynıdır. Bu özellik, donanım elemanlarını benzer şekilde tanımlama olanağı sağlar. Yani, dosya sistemi bu farklı donanım elemanlarını akış vasıtası ile benzer elemanlar gibi kullanabilir. Böylece, disk üzerindeki bir dosyaya yazı yazmak için kullandığınız fonksiyonları, aynı zamanda ekrana ve yazıcıya yazı yazmak için de kullanabiliriz.
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ımıza bilgi aktarabilir veya dosya içeriğini değiştirebiliriz.
2 tür akış vardır:
1. Metin akışı 2. İkili sistem 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. Metin akışı, metin dosyalarını satırları tek bir yeni satır karakteri (ASCII 10 = Line feed = LF = \n) ile ayrılmış olarak dikkate alır. Ancak, DOS metin dosyaları, her satırı arasında bir yeni satır (ASCII 10 = Line feed = LF = \n) ve bir satır başı (ASCII 13 = Carriage return = CR = \r) karakteri olmak üzere toplam iki karakter ile, diske kaydedilir. Metin akışına diskten bir dosya okurken yeni satır ve satır başı karakterleri tek bir yeni satır karakterine çevrilir. Metin akışından diske bir dosyayı kaydederken ise, tek bir yeni satır karakteri, yeni satır ve satır başı karakterlerine çevrilir. Bu nedenle, metin akışına gönderilen ifadelerle dosyaya yazılan ifadeler birebir aynı olmayabilir.
İkili sistem akışında ise, herhangi bir karakter dönüşümü yapılmaz. Yani, ikili akıştan diske bir dosya kaydederken veya diskten ikili bir dosya aktarırken, dosya içinde yer alan karakterlerde herhangi bir değişiklik yapılmaz. İkili sistem akışına gönderilen ifadelerle dosyaya gönderilen ifadeler tamamen birbirinin aynıdır.
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 sisteminde kullanılan dosya kavramı, normal bir disk dosyası için kullanılabileceği gibi, ekran, klavye gibi bilgisayar parçaları içinde kullanılabilir.
Bir dosya açıldığında, 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 kütüphane fonksiyonlarını kullanarak, dosya aktif konumunu başa alabilir veya belirli bir konuma taşıyabiliriz.
Bir programı çalıştırdığımızda, 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.
Bu standart akışlar FILE işaretçileri olduğundan, FILE değişkenini kullanan bütün fonksiyonlarla kullanılabilir.
Standart akışlar birer değişken olarak kabul edilmezler. fopen() ve fclose() fonksiyonları tarafından işleme tabi tutulamazlar. Bu akışları kullanabiliriz, ama değiştiremeyiz.
Standart akışlar yoluyla konsol ve dosya giriş/çıkış fonksiyonlarını birbirinin yerine kullanabiliriz. Aslında temel olarak bu fonksiyonlar arasında pek büyük bir fark yoktur.
Şimdi öğrendiklerimizi örnekler üzerinde incelemeye çalışalım:
#include <stdio.h>
#include <string.h>
int main(void)
{
char cdizi[40];
strcpy(cdizi, "Bilgisayar")
printf(cdizi);
return 0;
}
Program, aşağıdaki karakter dizisini ekrana 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ı yazarsak, çıkış (stdout) deneme.txt adlı dosyaya yönlendirildiğinden, program çıktısı deneme.txt adlı dosyaya aktarılır:
deneme > deneme.txt
#include <stdio.h>
char* bg_fgets(char *str, int count);
int main(void)
{
char cdizi[40];
printf("Bir karakter dizisi giriniz: ");
bg_fgets(cdizi, 40);
printf(cdizi);
return 0;
}
char* bg_fgets(char *str, int count)
{
const char *s;
fgets(str, count, stdin);
for (s=str; *s && *s!='\n'; ++s);
if ((s-str) < (count-1)) *(str+(s-str)) = '\0';
return str;
}
Program, klavyeden girilen karakter dizisini ekrana yazar. Programın derlenmiş adının deneme.exe olduğunu kabul edelim. Bu durumda, komut satırında aşağıdaki satırı yazarsak, çıkış (stdout) deneme.txt adlı dosyaya yönlendirildiğinden, program çıktısı deneme.txt adlı dosyaya aktarılır:
deneme > deneme.txt
bg_fgets() fonksiyonu, fgets() fonksiyonu ile klavyeden okunan karakter dizisi girilebilecek maksimum karakter sayısından az olduğunda, karakter dizisine '\0' karakterinden hemen önce otomatik olarak eklenen '\n' karakteri yerine '\0' karakteri koymak için kullanılır.
Komut satırında elde ettiğimiz .exe dosyayı > işareti yoluyla stdout akışı ile kullandığımızda, .exe dosyanın ekrana yazacağı değerler bir dosyaya yazılır.
Komut satırında elde ettiğimiz .exe dosyayı < işareti yoluyla stdin akışı ile kullandığımızda, .exe dosyaya klavyeden girilmesi beklenen değerler bir dosyadan okunur.
Şimdi, dosya açma ve kapama işlemleri ile 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 genel yapısı verilen fopen() fonksiyonu kullanılır:
FILE *fopen(char *dosya-adı, char *mod);
dosya-adı ifadesi açılacak dosya adını ve mod ifadesidosya açılış şeklini gösterir.
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. Dosya mevcut olmalıdır. |
w | Yazma için bir metin dosyası oluşturur. Aynı isimde bir dosya zaten mevcut ise, içeriği silinir. |
a | Bir metin dosyasını ekleme yapmak için açar. Dosya yok ise oluşturulur. |
r+ | Okuma ve yazma için bir metin dosyası açar. Dosya mevcut olmalıdır. |
w+ | Okuma ve yazma için bir metin dosyası oluşturur. |
a+ | Okuma ve ekleme için bir metin dosyası açar. |
rb | Okuma için bir ikili sistem dosyası açar. Dosya mevcut olmalıdır. |
wb | Yazma için bir ikili sistem dosyası oluşturur. Aynı isimde bir dosya zaten mevcut ise, içeriği silinir. |
ab | Bir ikili sistem dosyasını ekleme yapmak için açar. Dosya yok ise oluşturulur. |
r+b | Okuma ve yazma için bir ikili sistem dosyası açar. Dosya mevcut olmalıdır. |
w+b | Okuma ve yazma için bir ikili sistem dosyası oluşturur. |
a+b | Okuma ve ekleme için bir ikili sistem dosyası açar. |
Eğer dosya açma işlemi başarılı olarak sonuçlanırsa, fopen() fonksiyonu geçerli bir dosya işaretçisi geri döndürür. 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 yapıdır. 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 döndürür. 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 döndürür. 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 döndürmesi gerekir. Döndürülen değerin NULL olmadığından emin olmak için döndürülen değeri kontrol etmek gerekir.
Bir dosyanın açılmasını bir örnek üzerinde incelemeye çalışalım:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
if ((fp = fopen("dosya.txt", "r")) == NULL) {
printf("Dosya açma hatası!\n");
exit(1);
}
return 0;
}
Program, aşağıdaki satırı ekrana yazar:
Dosya açma hatası!
Programda kullanılan fopen() fonksiyonu NULL bir değer döndürür (dosya.txt adlı bir dosya olmadığından). Böylece, if koşulu sağlandığından, printf() işlem satırında yer alan karakter dizisini ekrana yazar. exit() fonksiyonu programın sona ermesini sağlar.
• Mevcut olmayan bir dosyayı sadece okuma (r) için açmak istediğimizde, fopen() fonksiyonu hata verir.
• Mevcut olmayan bir dosyayı ekleme yapmak (a) için açmak istediğimizde, program bir dosya oluşturur. Dosya zaten mevcut ise girdiğimiz bilgiler dosyanın sonuna eklenir. Dosyada önceden yer alan bilgiler ise değişmez.
• Mevcut olmayan bir dosyayı yazma işlemi (w) için açmak istediğimizde, program bir dosya oluşturur. Dosya zaten mevcutsa, içindeki bilgilerin tamamı silinir ve tamamen yeni bir dosya oluşturulur.
• r+ modunu kullandığınızda, dosya mevcut değilse, yeni bir dosya oluşturulmaz. Aynı koşullarda w+ modu ise yeni bir dosya oluşturur. Dosya mevcut ise, dosyayı w+ modu ile açmamız dosyanın içeriğini tamamen siler, fakat r+ modu ile açmak silmez.
Bir dosyayı kapatmak için aşağıda genel yapısı verilen fclose() fonksiyonu kullanılır:
int fclose (FILE *fp);
fp ifadesi fopen() ile kullanılan dosya işaretçisini gösterir. fclose() fonksiyonu fp ile bağlantılı dosyayı kapatır ve dosyanın akış ile olan bağlantısını keser.
Sistemin etkinliğini artırmak için dosya sistem uygulamalarının çoğu veriyi diske her defasında bir sektör olmak üzere yazarlar. Bu durumda, bir sektör değere erişilene dek, veri tampon belleğe aktarılır daha sonra diske yazılır. fclose() fonksiyonunu çağırdığımızda, fclose() fonksiyonu tam dolmamış tampon bellek içeriğini otomatik olarak diske yazar. Bu işleme TAMPON BELLEK BOŞALTMA adı verilir.
Eğer fclose() fonksiyonunu uygun olmayan argümanlarla çağırırsak, dosya sistemimiz zarar görebilir, tekrar elde edilmesi mümkün olmayan veri kayıpları meydana gelebilir.
fclose() fonksiyonu normal olarak çalışırsa 0, bir hata meydana gelirse EOF değerini geri döndürür.
Bir dosya açıldıktan sonra, açılış moduna bağlı olarak, aşağıda genel yapısı verilen iki fonksiyonu kullanarak dosyaya bir karakter yazabilir veya dosyadan bir karakter okuyabiliriz:
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 döndürür. fgetc() fonksiyonu bir hata durumunda ve dosya sonuna geldiğinde int bir değer olan EOF değerini döndürdüğü için, geri döndürülen değerin kontrolü amacıyla fgetc() fonksiyonu int bir değer geri döndürür. Ancak, fgetc() fonksiyonunun geri döndürdüğü değeri int bir değişkene atamamız şart değildir. Geri döndürülen değeri bir karakter değişkene de atayabiliriz.
fputc() fonksiyonu id değişken değeri olan byte'ı fp ile gösterilen dosyaya unsigned char olarak yazar. id değişkenini, int bir değer olarak tanımlandığı halde, char bir değer olarak çağırabiliriz. Dosyaya yazma işlemi başarılı olursa, fputc() fonksiyonu yazılan karakteri aksi takdirde EOF değerini geri döndürür.
fgetc() yerine getc() ifadesini, fputc() fonksiyonu yerine ise putc() ifadesini kullanabiliriz. Sonuçta, fonksiyonlar tarafından yapılan işlem tamamen birbirinin aynıdır.
Şimdi, öğrendiklerimizi örnekler üzerinde incelemeye çalışalım:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
int id;
if ((fp = fopen ("dosya.txt", "w")) == NULL) {
printf("Dosya açma hatası!");
exit(1);
}
for (id=0; id<10; id++) fputc ('a', fp);
fclose(fp);
if ((fp = fopen ("dosya.txt", "r")) == NULL) {
printf("Dosya açma hatası!");
exit(1);
}
for (id=0; id<10; id++) printf("%c", fgetc(fp));
fclose(fp);
return 0;
}
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.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
int id;
if ((fp = fopen ("dosya.txt", "w")) == NULL) {
printf("Dosya açma hatası!");
exit(1);
}
for (id=0; id<10; id++) fputc ('b', fp);
fclose(fp);
if ((fp = fopen ("dosya.txt", "a")) == NULL) {
printf("Dosya açma hatası!");
exit(1);
}
for (id=0; id<10; id++) fputc ('c', fp);
fclose(fp);
if ((fp = fopen ("dosya.txt", "r")) == NULL) {
printf("Dosya açma hatası!");
exit(1);
}
for (id=0; id<20; id++) printf("%c", fgetc(fp));
fclose(fp);
return 0;
}
Program, aşağıdaki satırı ekrana yazar:
bbbbbbbbbbcccccccccc
Program, fputc() fonksiyonunu kullanarak, her defasında bir karakter olmak üzere, 10 adet 'b' harfini w modunda açılan dosyaya yazar. Bu işlemi bitirdikten sonra, dosyayı kapatır. Dosyayı a modunda açarak, bu kez 10 adet 'c' harfini dosyanın sonuna ekler. Dosyayı tekrar kapatır. Dosyayı son kez r modunda açtıktan sonra, her karakteri birer birer dosyadan okuyarak ekrana yazar.
#include <stdio.h>
#include <stdlib.h>
char* bg_fgets(char *str, int count);
int main(void)
{
char cdizi[41];
FILE *fp;
char *cp;
int id;
printf("Bir karakter dizisi giriniz (En fazla 40 karakter): ");
bg_fgets(cdizi, 41);
if ((fp = fopen("dosya.txt", "w")) == NULL) {
printf("Dosya açma hatası!\n");
exit(1);
}
cp = cdizi;
while (*cp) {
if (fputc (*cp, fp) == EOF) {
printf("Dosyaya yazma hatası!\n");
exit(1);
}
cp++;
}
fclose (fp);
if ((fp = fopen("dosya.txt", "r")) == NULL) {
printf("Dosya açma hatası!\n");
exit(1);
}
for ( ; ; ) {
id = fgetc(fp);
if (id == EOF) break;
putchar(id);
}
fclose (fp);
return 0;
}
char* bg_fgets(char *str, int count)
{
const char *s;
fgets(str, count, stdin);
for (s=str; *s && *s!='\n'; ++s);
if ((s-str) < (count-1)) *(str+(s-str)) = '\0';
return str;
}
Yukarıdaki örnekte, program klavyeden girilen bir karakter dizisini cdizi adlı bir karakter dizisine atar. Dizi içeriğini w modu ile açtığı dosya.txt adlı bir metin dosyasına yazar ve dosyayı kapatır. Dosyayı bu kez sadece okumak için r modunda açar. Dosya içeriğini okuyup ekrana yazdıktan sonra tekrar dosyayı kapatır.
bg_fgets() fonksiyonu, fgets() fonksiyonu ile klavyeden okunan karakter dizisi girilebilecek maksimum karakter sayısından az olduğunda, karakter dizisine '\0' karakterinden hemen önce otomatik olarak eklenen '\n' karakteri yerine '\0' karakteri koymak için kullanılır.
#include <stdio.h>
#include <stdlib.h>
char* bg_fgets(char *str, int count);
int main(void)
{
char cdizi[41];
FILE *fp;
char *cp;
char cd; // 1
printf("Bir karakter dizisi giriniz (En fazla 40 karakter): ");
bg_fgets(cdizi, 41);
if ((fp = fopen("dosya.txt", "w")) == NULL) {
printf("Dosya açma hatası!\n");
exit(1);
}
cp = cdizi;
while (*cp) {
if (fputc (*cp, fp) == EOF) {
printf("Dosyaya yazma hatası!\n");
exit(1);
}
cp++;
}
fclose (fp);
if ((fp = fopen("dosya.txt", "r")) == NULL) {
printf("Dosya açma hatası!\n");
exit(1);
}
for ( ; ; ) {
cd = fgetc(fp);
if (cd == EOF) break;
putchar(cd);
}
fclose (fp);
return 0;
}
char* bg_fgets(char *str, int count)
{
const char *s;
fgets(str, count, stdin);
for (s=str; *s && *s!='\n'; ++s);
if ((s-str) < (count-1)) *(str+(s-str)) = '\0';
return str;
}
Yukarıdaki örnekte yer alan programın bir önceki örnekten tek farkı 1 sayısı ile gösterilen işlem satırında yer alan değişkenin int değil char bir değer olarak tanımlanmış olmasıdır. Bu durumda, fgetc() fonksiyonu tarafından geri verilen değer char bir değişkene atanmış olur. char ve int değerlere aynı şekilde işlem yapıldığından, EOF değerinin kontrolü sağlanır.
bg_fgets() fonksiyonu, fgets() fonksiyonu ile klavyeden okunan karakter dizisi girilebilecek maksimum karakter sayısından az olduğunda, karakter dizisine '\0' karakterinden hemen önce otomatik olarak eklenen '\n' karakteri yerine '\0' karakteri koymak için kullanılır.
#include <stdio.h>
#include <stdlib.h>
char* bg_fgets(char *str, int count);
int main(void)
{
char cdizi[41];
FILE *fp;
char *cp;
char cd;
printf("Bir karakter dizisi giriniz (En fazla 40 karakter): ");
bg_fgets(cdizi, 41);
if ((fp = fopen("dosya.txt", "w")) == NULL) {
printf("Dosya açma hatası!\n");
exit(1);
}
cp = cdizi;
while (*cp) {
if (fputc (*cp, fp) == EOF) {
printf("Dosyaya yazma hatası!\n");
exit(1);
}
cp++;
}
fclose (fp);
if ((fp = fopen("dosya.txt", "r")) == NULL) {
printf("Dosya açma hatası!\n");
exit(1);
}
for ( ; ; ) {
if ((cd = fgetc(fp)) == EOF) break; // 1
putchar(cd);
}
fclose (fp);
return 0;
}
char* bg_fgets(char *str, int count)
{
const char *s;
fgets(str, count, stdin);
for (s=str; *s && *s!='\n'; ++s);
if ((s-str) < (count-1)) *(str+(s-str)) = '\0';
return str;
}
Yukarıdaki örnekte yer alan programın bir önceki örnekten tek farkı 1 sayısı ile gösterilen işlem satırında, önceki programdaki 2 işlem satırının birleştirilmiş olmasıdır. Bu durumda fgetc() fonksiyonunun geri verdiği değerin cd değişkenine atanma işlemi ile değişken değerinin EOF değeri ile karşılaştırılması aynı işlem satırında yapılmış olur.
bg_fgets() fonksiyonu, fgets() fonksiyonu ile klavyeden okunan karakter dizisi girilebilecek maksimum karakter sayısından az olduğunda, karakter dizisine '\0' karakterinden hemen önce otomatik olarak eklenen '\n' karakteri yerine '\0' karakteri koymak için kullanılır.
#include <stdio.h>
#include <stdlib.h>
char* bg_fgets(char *str, int count);
int main(void)
{
char cdizi[41];
FILE *fp;
char *cp;
char cd;
printf("Bir karakter dizisi giriniz (En fazla 40 karakter): ");
bg_fgets(cdizi, 41);
if ((fp = fopen("dosya.txt", "w")) == NULL) {
printf("Dosya açma hatası!\n");
exit(1);
}
cp = cdizi;
while (*cp) {
if (fputc (*cp++, fp) == EOF) { // 1
printf("Dosyaya yazma hatası!\n");
exit(1);
}
}
fclose (fp);
// Okumak için bir metin dosyası açar.
if ((fp = fopen("dosya.txt", "r")) == NULL) {
printf("Dosya açma hatası!\n");
exit(1);
}
// Dosyanın içeriğini gösterme ve dosyayı kapatma.
while ((cd = fgetc(fp)) != EOF) putchar(cd); // 2
fclose (fp);
return 0;
}
char* bg_fgets(char *str, int count)
{
const char *s;
fgets(str, count, stdin);
for (s=str; *s && *s!='\n'; ++s);
if ((s-str) < (count-1)) *(str+(s-str)) = '\0';
return str;
}
Yukarıdaki örnekte yer alan programın bir önceki örnekten farkı, 1 sayısı ile gösterilen işlem satırına bir önceki örnekte yer alan cp++ işlem satırının ilave edilmiş olması ve bir önceki örnekte dosyadan okuma işlemi için kullanılan for döngüsü yerine 2 sayısı ile gösterilen işlem satırında while döngüsü kullanılmış olmasıdır.
bg_fgets() fonksiyonu, fgets() fonksiyonu ile klavyeden okunan karakter dizisi girilebilecek maksimum karakter sayısından az olduğunda, karakter dizisine '\0' karakterinden hemen önce otomatik olarak eklenen '\n' karakteri yerine '\0' karakteri koymak için kullanılır.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp;
char cd;
int id = 0;
if (argc != 3) {
printf("Kullanım : deneme <dosya adı> <harf>");
exit(1);
}
if ((fp = fopen(argv[1], "r")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
while ((cd = fgetc(fp)) != EOF) {
if (cd == *argv[2]) {
printf("%c bulundu\n", cd);
id++;
}
}
printf("\nAradığınız harften dosyada %d adet bulunmaktadır.", id);
fclose(fp);
return 0;
}
Yukarıdaki örnekte, program komut satırında adını verdiğiniz dosyada, yine komut satırından girdiğiniz bir harfin mevcut olup olmadığını araştırır. Aradığınız karakter dosyada yoksa herhangi bir işlem yapmaz. Eğer varsa söz konusu karakteri her bulmasında ve son olarak karakterin kaç kez tekrarlandığını ekrana yazar.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp;
char cd;
if (argc!=2) {
printf("Kullanım: deneme <dosya adı>");
exit(1);
}
if ((fp = fopen(argv[1], "r")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
while ((cd = fgetc(fp)) != EOF) putchar(cd);
fclose(fp);
return 0;
}
Program, komut satırından adı girilen dosyanın içeriğini ekrana yazar.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp;
char cd;
int id = 0;
if (argc!=2) {
printf("Kullanım: deneme <dosya adı>");
exit(1);
}
if ((fp= fopen(argv[1], "r")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
while ((cd = fgetc(fp)) != EOF) {
putchar(cd);
id++;
}
printf("\nDosyada bulunan toplam karakter sayısı: %d", id);
fclose(fp);
return 0;
}
Program, komut satırından adını gireceğiniz dosyanın içeriğini ve dosyada bulunan toplam karakter sayısını ekrana yazar.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp1, *fp2;
char cd;
if (argc!=3) {
printf("Kullanım: deneme <Kaynak dosya> <Hedef dosya>");
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 ((cd = fgetc(fp1)) != EOF) fputc (cd, fp2);
fclose (fp1);
fclose (fp2);
return 0;
}
Pprogram, komut satırından girilen kaynak dosyanın içeriğini hedef dosyaya yazar. Hedef dosya mevcut değilse oluşturur, mevcut ise önceki içeriğini tamamen siler.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp1, *fp2, *fp3;
char cd;
if (argc!=3) {
printf("Kullanım: deneme <Kaynak dosya> <Hedef dosya>");
exit(1);
}
if ((fp2 = fopen(argv[2], "rb")) == NULL) {
printf("Hedef dosya açılamadı!\n");
exit(1);
}
if ((fp3 = fopen("dosya1.txt", "wb")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
while ((cd = fgetc(fp2)) != EOF) fputc (cd, fp3);
fclose (fp2);
fclose (fp3);
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 ((cd = fgetc(fp1)) != EOF) fputc (cd, fp2);
fclose (fp1);
fclose (fp2);
if ((fp3 = fopen("dosya1.txt", "rb")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
if ((fp1 = fopen(argv[1], "wb")) == NULL) {
printf("Kaynak dosya açılamadı!\n");
exit(1);
}
while ((cd = fgetc(fp3)) != EOF) fputc (cd, fp1);
fclose (fp1);
fclose (fp3);
return 0;
}
Program, komut satırından adları girilen iki dosyanın içeriğini birbiri ile değiştirir.
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 bilemeyiz. EOF değerinin fgetc() fonksiyonu tarafından hangi nedenle geri verildiğini anlayabilmek amacıyla aşağıda ana yapıları verilen feof() ve ferror() fonksiyonlarını programlarımızda kullanabiliriz:
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 kullanabiliriz.
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>
int main(int argc, char *argv[])
{
FILE *fp1, *fp2;
char cd;
if (argc!=3) {
printf("Kullanım: deneme <Kaynak dosya> <Hedef dosya>");
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)) {
cd = fgetc(fp1);
if (ferror(fp1)) {
printf("Kaynak dosyadan okuma hatası!\n");
exit(1);
}
if (!feof(fp1)) fputc (cd, fp2);
if (ferror(fp2)) {
printf("Hedef dosyaya yazma hatası!\n");
exit(1);
}
}
fclose (fp1);
fclose (fp2);
return 0;
}
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.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp1, *fp2;
char cd1, cd2;
int id = 1;
if (argc!=3) {
printf("Kullanım: deneme <Dosya adı> <Dosya adı>");
exit(1);
}
if ((fp1=fopen(argv[1], "rb")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
if ((fp2=fopen(argv[2], "rb")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
while (!feof(fp1)) {
cd1 = fgetc(fp1);
if (ferror(fp1)) {
printf("İlk dosyada okuma hatası!\n");
exit(1);
}
cd2 = fgetc (fp2);
if (ferror(fp2)) {
printf("İkinci dosyada okuma hatası!\n");
exit(1);
}
if (cd1 == cd2) {
printf("Dosyaların %d nci karakterleri aynıdır!\n", id);
break;
}
id++;
}
fclose(fp1);
fclose(fp2);
return 0;
}
Program, komut satırından argüman olarak verilen kaynak dosya ile hedef dosyanın içeriğini karşılaştırır. Birbirine benzeyen ilk karakteri bulduğunda, karakterin dosya içindeki sıra numarasını ekrana yazar ve program sona erer.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp;
unsigned ud;
if (argc!=2) {
printf("Kullanım: deneme <Dosya adı>");
exit(1);
}
if ((fp=fopen(argv[1], "rb")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
ud = 0;
while (!feof(fp)) {
fgetc(fp);
if (ferror(fp)) {
printf("Dosyadan okuma hatası!\n");
exit(1);
}
ud++;
}
printf("Verilen dosya uzunluğu %u byte'dır!\n", ud);
fclose (fp);
return 0;
}
Program, komut satırından argüman olarak verilen dosyanın uzunluğunu hesaplayarak ekrana yazar.
Metin dosyaları ile çalışırken aşağıda genel yapıları verilen dört fonksiyonla farklı veri türleri ile işlemler gerçekleştirebiliriz:
int fputs (char *cdizi, FILE *fp);
char *fgets (char *cdizi, int id, 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 id değişkeni ise, dosyadan okunacak karakter sayısını belirler. fgets() fonksiyonu, id-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>
char* bg_fgets(char *str, int count);
int main(int argc, char *argv[])
{
FILE *fp;
char cdizi[80];
if (argc!=2) {
printf("Kullanım: deneme <Dosya adı>");
exit(1);
}
if ((fp=fopen(argv[1], "w")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
do {
printf("Bir karakter dizisi giriniz: ");
bg_fgets(cdizi, 80); // fgets() fonksiyonu ile klavyeden karakter okuma
strcat(cdizi, "\n");
if (*cdizi!='\n') fputs(cdizi, fp);
} while (*cdizi!='\n');
fclose (fp);
if ((fp=fopen(argv[1], "r")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
do {
if (fgets(cdizi, 80, fp)) printf(cdizi); // fgets() fonksiyonu ile dosyadan karakter okuma
} while (!feof(fp));
fclose (fp);
return 0;
}
// fgets() fonksiyonu ile klavyeden okunan karakter dizisi girilebilecek maksimum karakter sayısından az olduğunda
// karakter dizisine '\0' karakterinden hemen önce otomatik olarak eklenen '\n' karakteri yerine '\0' karakteri koyar.
char* bg_fgets(char *str, int count)
{
const char *s;
fgets(str, count, stdin);
for (s=str; *s && *s!='\n'; ++s);
if ((s-str) < (count-1)) *(str+(s-str)) = '\0';
return str;
}
Pprogram, fputs() ve fgets() fonksiyonlarının çalışma şeklini göstermektedir. Klavyeden girilen karakter dizilerini, komut satırından argüman olarak verilen dosyaya yazar. Bir karakter dizisi girilirken ENTER tuşuna basıldığında, dosyaya yazma işlemi sona erer ve dosya kapanır. Sonra, program dosyayı tekrar açar. Girdiğiniz karakter dizilerini sıra ile okuyarak ekrana yazar. bg_fgets() fonksiyonu, fgets() fonksiyonu ile klavyeden okunan karakter dizisi girilebilecek maksimum karakter sayısından az olduğunda karakter dizisine '\0' karakterinden hemen önce otomatik olarak eklenen '\n' karakteri yerine '\0' karakteri koymak için kullanılır.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp;
char cdizi[40];
double dd;
int id;
char cd;
if (argc!=2) {
printf("Kullanım: deneme <Dosya adı>");
exit(1);
}
if ((fp=fopen(argv[1], "w")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
fprintf (fp,"%f %d %s %c", 654.123, 9852, "Bilgisayar", 'A');
fclose (fp);
if ((fp=fopen (argv[1], "r")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
fscanf (fp,"%lf%d%s %c", &dd, &id, cdizi, &cd);
printf("%lf %d %s %c", dd, id, cdizi, cd);
fclose (fp);
return 0;
}
Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri 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.
Şimdi, öğrendiklerimizi örneklerle pekiştirmeye çalışalım:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp1, *fp2;
char cdizi[100];
if (argc!=3) {
printf("Kullanım: deneme <Kaynak dosya> <Hedef dosya>");
exit(1);
}
if ((fp1=fopen(argv[1], "r")) == NULL) {
printf("Kaynak dosya açılamadı!\n");
exit(1);
}
if ((fp2=fopen(argv[2], "w")) == NULL) {
printf("Hedef dosya açılamadı!\n");
exit(1);
}
while (!feof(fp1)) {
fgets(cdizi, 100, fp1);
if (ferror(fp1)) {
printf("Dosyadan okuma hatası!");
break;
}
fputs(cdizi, fp2);
if (ferror(fp2)) {
printf("Dosyaya yazma hatası!\n");
break;
}
}
fclose (fp1);
fclose (fp2);
return 0;
}
Program, fgets() ve fputs() fonksiyonları ile ilgili bir çalışmayı göstermektedir. Program, komut satırından argüman olarak verilen kaynak dosyanın içeriğini, yine komut satırından argüman olarak verilen hedef dosyaya kopyalar. Bu arada, tam bir hata kontrolü sağlanır.
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
char* bg_fgets(char *str, int count);
int main(void)
{
FILE *fp;
int id;
char cd;
char cdizi[21];
printf("Bir karakter giriniz: ");
cd = getche();
printf("\nBir karakter dizisi giriniz (En fazla 20 karakter): ");
bg_fgets(cdizi, 21);
if ((fp=fopen("deneme.txt", "w")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
id = fprintf (fp, "%s %c", cdizi, cd);
printf("Dosyaya yazılan karakter sayısı: %d\n", id);
fclose(fp);
if ((fp=fopen("deneme.txt", "r")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
id = fscanf (fp, "%s %c", cdizi, &cd);
printf("\n%c %s\n", cd, cdizi);
printf("Dosyadan okunan alan sayısı: %d", id);
fclose (fp);
return 0;
}
char* bg_fgets(char *str, int count)
{
const char *s;
fgets(str, count, stdin);
for (s=str; *s && *s!='\n'; ++s);
if ((s-str) < (count-1)) *(str+(s-str)) = '\0';
return str;
}
Pprogram, klavyeden girilen bir karakter ile bir karakter dizisini bir dosyaya yazar. Dosyaya yazdığı karakter sayısını ekrana yazar. Sonra, dosyadan okuduğu değerlerle okunan alan sayısını ekrana yazar. Dosyaya yazılan karakter sayısı fprintf() fonksiyonunun geri verdiği değer, dosyadan okunan alan sayısı ise fscanf() fonksiyonunun geri verdiği değeri kullanarak ekrana yazar.
bg_fgets() fonksiyonu, fgets() fonksiyonu ile klavyeden okunan karakter dizisi girilebilecek maksimum karakter sayısından az olduğunda, karakter dizisine '\0' karakterinden hemen önce otomatik olarak eklenen '\n' karakteri yerine '\0' karakteri koymak için kullanılır.
Verileri dosyaya yazma ve dosyadan okuma işlemlerinde aşağıda genel yapıları verilen fread() ve fwrite() fonksiyonlarını da kullanabiliriz:
size_t fread (void *tampon-bellek, size_t boyut, size_t id, FILE *fp);
size_t fwrite (void *tampon-bellek, size_t boyut, size_t id, 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 id kadar değeri tampon-bellek ile gösterilen belleğe atar. Burada, boyut ifadesi okunan verinin byte olarak değerini, id 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 id kadar değeri fp ile gösterilen dosyaya yazar. Burada, boyut ifadesi yazılan byte olarak değerini, id 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 id 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çilerini, fread() ve fwrite() fonksiyonlarında olduğu gibi, herhangi bir veri türü ile birlikte kullanabiliriz. 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 benzetebiliriz. 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>
int main(void)
{
FILE *fp;
int id;
if ((fp=fopen("deneme.txt", "w")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
printf("Bir int değer giriniz: ");
scanf("%d", &id);
if (fwrite(&id, 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(&id, sizeof(int), 1, fp) != 1) {
printf("Okuma hatası!\n");
exit(1);
}
printf("Dosyadan okunan değişken değeri: %d", id);
fclose(fp);
return 0;
}
Program, fread() ve fwrite() fonksiyonlarının çalışmasını göstermektedir. Program, klavyeden bir int değer girilmesini ister. Girilen değeri id değişkenine atar. Sonra, fwrite() fonksiyonunu kullanarak id değişken değerini deneme.txt dosyasına yazar. Daha sonra, fread() fonksiyonu ile aynı değeri dosyadan okutarak ekrana yazar.
Yukarıdaki örnekte, int değişken boyutunu belirlemek için sizeof ifadesi kullanılmaktadır. Aşağıda kullanım şekli verilen sizeof ifadesi derleme zamanında devreye girer ve kendisinden sonra gelen veri türünün veya değişkenin boyutunu geri verir:
sizeof (veri-türü) sizeof değişken-adı;
sizeof ifadesi veri türü ile kullanıldığında parantezlerle birlikte tanımlanmasına rağmen, değişken adı ile kullanıldığı zaman, parantezlere gerek yoktur.
Şimdi, öğrendiklerimizi örneklerle pekiştirmeye çalışalım:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
int idizi[5];
int id;
for (id=0; id<5; id++) {
printf("int bir değer giriniz: ");
scanf("%d", &idizi[id]);
}
if ((fp=fopen ("deneme.txt", "wb")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
for (id=0; id<5; id++) {
if (fwrite(&idizi[id], sizeof(int), 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 (id=0; id<5; id++) {
idizi[id] = 0;
printf("%d ", idizi[id]);
}
printf("\n");
for (id=0; id<5; id++) {
if (fread(&idizi[id], sizeof(int), 1, fp) != 1) {
printf("Okuma hatası!\n");
exit(1);
}
}
fclose(fp);
printf("Dosyadan okunan dizi değerleri:\n");
for (id=0; id<5; id++) printf("%d ", idizi[id]);
return 0;
}
Program, girilen int değerleri 5 elemanlık bir diziye atar. Dizi değerlerini deneme.txt adlı bir dosyaya yazar. Dizi elemanlarına 0 değerini verir ve eleman değerlerini ekrana yazar. Daha sonra dosyaya yazdığı değerleri okur ve tekrar diziye atar. Son olarak dizi elemanlarını ekrana yazar.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
int idizi[5];
int id;
for (id=0; id<5; id++) {
printf("int bir değer giriniz: ");
scanf("%d", &idizi[id]);
}
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 (id=0; id<5; id++) {
idizi[id] = 0;
printf("%d ", idizi[id]);
}
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 (id=0; id<5; id++) printf("%d ", idizi[id]);
return 0;
}
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.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp1, *fp2;
int id1, id2;
if ((fp1=fopen("deneme1.txt", "wb")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
if ((fp2=fopen("deneme2.txt", "wb")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
for (id1=1, id2=0; id1>0 && id1<100; id2++) {
printf("Bir sayı giriniz (0-100): ");
scanf("%d", &id1);
fwrite (&id1, sizeof id1, 1, fp1);
}
fwrite (&id2, sizeof id2, 1, fp2);
fclose(fp1);
fclose(fp2);
printf("\n");
if ((fp1=fopen("deneme1.txt", "rb")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
if ((fp2=fopen("deneme2.txt", "rb")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
fread (&id2, sizeof id2, 1, fp2);
for ( ; id2>0; id2--) {
fread (&id1, sizeof id1, 1, fp1);
printf("%d\n", id1);
}
fclose(fp1);
fclose(fp2);
return 0;
}
Program, 0-100 arasında girilen sayıları teker teker deneme1.txt adlı bir dosyaya yazar. Yazdığı değişken sayısını ise deneme2.txt adlı bir dosyaya yazar. Sonra, dosyaya yazdığı değerleri birer birer okuyarak ekrana yazar. Eğer girilen sayı 0 ile 100 arasında değil ise, program sona erer.
fwrite() fonksiyonu akışa bir karakter dizisi yazarken karakter dizisinin sonuna otomatik olarak boş sonlandırıcı karakteri ('\0') eklemediğinden, yazılan karakter dizisi fread() fonksiyonu ile dosyadan okunduktan sonra, '\0' karakterinin program ile ayrıca karakter dizisi sonuna eklenmesi gerekir. Konuyu bir örnek üzerinde incelemeye çalışalım:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
FILE *fp;
char cdizi[]="Bilgisayar";
char gdizi[40];
if ((fp=fopen("dosya.txt", "w+")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
if (fwrite(cdizi, strlen(cdizi), 1, fp) != 1) {
printf("Dosyaya yazma hatası!\n");
exit(1);
}
rewind(fp);
if (fread(gdizi, strlen(cdizi), 1, fp) != 1) {
printf("Dosyadan okuma hatası!\n");
exit(1);
}
/* Okunan karakter dizisi sonuna boş karakter ekleme */
gdizi[strlen(cdizi)] = '\0';
printf("%s", gdizi);
fclose(fp);
return 0;
}
Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:
Bilgisayar
Program, yazma ve okuma modunda açtığı dosyaya fwrite() fonksiyonu ile yazdığı karakter dizisini, dosyanın konumunu başa aldıktan sonra, fread() fonksiyonu ile dosyadan okuyarak, sonuna boş karakter ekledikten sonra, ekrana yazar.
Ş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 ana yapısı verilen fseek() fonksiyonunu kullanarak bir dosyanın herhangi bir yerindeki bir bilgiyi okuyabilir veya değiştirebiliriz:
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ıdaki 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 döndürür.
Ayrıca, aşağıda ana yapısı verilen ftell() fonksiyonunu kullanarak bir dosyanın aktif konumunu belirleyebiliriz:
long ftell (FILE *fp);
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 belirlememiz ve fseek() fonksiyonunu SEEK_SET ile birlikte tanımlamamız gerekir.
Rastgele erişim metodunu ikili sistem dosyası olarak açılan metin dosyalarına uygulayabiliriz, ancak metin dosyası olarak açılan dosyalara uygulayamayız.
Şimdi, öğrendiklerimizi örnekler üzerinde incelemeye çalışalım:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
long int lid1, lid2;
int id;
FILE *fp;
if (argc!=2) {
printf("Kullanım : deneme <Dosya adı>");
exit(1);
}
if ((fp=fopen(argv[1], "rb")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
/* Dosya sonuna ulaşıp dosya boyutunu kaydeder. */
fseek (fp, 0L, SEEK_END);
lid2 = ftell(fp);
for ( ; ; ) {
printf("Byte sıra no.sunu giriniz: ");
scanf("%ld", &lid1);
if (lid1>=lid2) break;
if (fseek(fp, lid1, SEEK_SET)) {
printf("Arama hatası!");
exit(1);
}
id = getc(fp);
printf("%ld konumundaki değer ASCII %d : %c\n", lid1, id, id);
}
fclose(fp);
return 0;
}
Program, komut satırından argüman olarak girilen dosyayı açar. Girilen byte'da yer alan karakteri ve ASCII kodunu ekrana yazar. Dosya boyutundan fazla bir değer girerseniz program sona erer.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp1, *fp2;
long int lid;
if (argc!=3) {
printf("Kullanım : deneme <Dosya adı> <Dosya adı>");
exit(1);
}
if ((fp1=fopen(argv[1], "rb")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
if ((fp2=fopen(argv[2], "wb")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
fseek (fp1, 0L, SEEK_END);
lid = ftell(fp1);
for (lid=lid-1; lid>=0L; lid--) {
fseek (fp1, lid, SEEK_SET);
fputc(fgetc(fp1), fp2);
}
fclose(fp1);
fclose(fp2);
return 0;
}
Program, komut satırından adı verilen ilk dosyanın içeriğini yine komut satırından adı argüman olarak verilen ikinci dosyaya tersten kopyalar.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
int idizi[10];
int id1, id2;
if ((fp=fopen("deneme.txt", "wb")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
for (id1=0; id1<10; id1++) idizi[id1] = (id1+1)*4;
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("Dosyanın kaçıncı elemanının değerini istiyorsunuz?: ");
scanf("%d", &id1);
if (fseek(fp, id1*sizeof(int), SEEK_SET)) {
printf("Arama hatası!\n");
exit(1);
}
fread (&id2, sizeof(int), 1, fp);
printf("%d nci eleman değeri: %d", id1, id2);
fclose(fp);
return 0;
}
Program, 10 adet int değeri idizi adlı bir diziye atar. Diziyi ise deneme.txt adlı bir dosyaya yazar. Sonra, dosyadan istediğiniz sıradaki değeri okur ve ekrana yazar.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp;
long int lid;
if (argc!=2) {
printf("Kullanım : deneme <Dosya adı>");
exit(1);
}
if ((fp=fopen(argv[1], "rb")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
fseek (fp, 0, SEEK_END) ;
lid = ftell(fp);
fseek (fp, 0, SEEK_SET);
for ( ; lid>=0; lid=lid-1L) putchar(fgetc(fp));
fclose(fp);
return 0;
}
Program, komut satırından verilen metin dosyasının içeriğini ekrana yazar.
Aşağıda ana yapısı verilen remove() fonksiyonunu bir dosyayı silmek için kullanabiliriz:
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 döndürür.
Bir dosyanın aktif konumunu dosya başına almak için aşağıda genel yapısı verilen rewind() fonksiyonunu kullanabiliriz:
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.
Bir dosya ile ilgili tampon belleği boşaltmak için aşağıda ana yapısı verilen fflush() fonksiyonunu kullanabiliriz:
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. fflush() fonksiyonunu argüman olmadan kullandığımızda, mevcut bütün disk tampon bellekleri silinir.
Şimdi, öğrendiklerimizi örnekler üzerinde incelemeye çalışalım:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <conio.h>
int main(int argc, char *argv[])
{
if (argc != 2) {
printf("Kullanım : deneme <Dosya adı>");
exit(1);
}
printf("Dosya silinecek! Devam edecek misiniz (E/H)?");
if (toupper(getche())=='E') remove(argv[1]);
return 0;
}
Program, komut satırından argüman olarak girilen dosyayı siler.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp;
if (argc!=2) {
printf("Kullanım : deneme <Dosya adı>n");
exit(1);
}
if ((fp=fopen(argv[1], "r")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
while (!feof(fp)) putchar(getc(fp));
rewind(fp);
printf("\n");
while (!feof(fp)) putchar(getc(fp));
fclose(fp);
return 0;
}
Program, komut satırından argüman olarak verilen dosya içeriğini ekrana yazar. Aynı dosyanın aktif konumunu rewind() fonksiyonu ile başa aldıktan sonra dosya içeriğini bir kez daha ekrana yazar.
Şimdi, örneklerle öğrendiklerimizi pekiştirmeye çalışalım:
#include <stdio.h>
int main(void)
{
char cd;
while (!feof(stdin)) {
scanf("%c", &cd);
printf("%c", cd);
}
return 0;
}
Programın derlenmiş adını deneme.exe kabul ederek, komut satırından aşağıdaki satırı girersek, program deneme1.txt dosyası içeriğini deneme2.txt dosyasına kopya eder:
deneme < deneme1.txt > deneme2.txt
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <conio.h>
int main(int argc, char *argv[])
{
FILE *fp;
char cdizi[101];
if (argc!=2) {
printf("Kullanım : deneme <Dosya adı>");
exit(1);
}
if ((fp=fopen(argv[1], "r")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
while (!feof(fp)) {
fgets(cdizi, 101, fp);
printf("%s", cdizi);
}
fclose(fp1);
return 0;
}
Program, komut satırından adı argüman olarak verilen dosyanın içeriğini 100'er karakterlik paketler halinde okuyarak ekrana yazar.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
int idizi[10];
int id;
if ((fp=fopen("deneme.txt", "wb")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
for (id=0; id<10; id++) {
idizi[id] = id*id;
fwrite (&idizi[id], sizeof (int), 1, fp);
}
fclose (fp);
for (id=0; id<10; id++) {
idizi[id] = 1;
printf("%d ", idizi[id]);
}
printf("\n");
if ((fp=fopen("deneme.txt", "rb")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
for (id=0; id<10; id++) {
fread (&idizi[id], sizeof (int), 1, fp);
printf("%d ", idizi[id]);
}
fclose (fp);
return 0;
}
Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:
1 1 1 1 1 1 1 1 1 1 0 1 4 9 16 25 36 49 64 81
Program, 1'den 10'a kadar olan sayıların karelerini idizi adlı bir diziye kaydeder. Dizi elemanlarını deneme.txt dosyasına birer birer yazar ve dosyayı kapatır. idizi dizi elemanlarının hepsine 1 değerini atar ve dizi değerlerini ekrana yazar. Sonra, dosyayı bu kez okuma işlemi için açar. Dosyadaki değerleri yine birer birer idizi adlı diziye atar ve dizi eleman değerlerini ekrana yazar.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
int idizi[10];
int id;
if ((fp=fopen("deneme.txt", "wb")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
for (id=0; id<10; id++) idizi[id] = id*id;
fwrite (idizi, sizeof (idizi), 1, fp);
fclose (fp);
for (id=0; id<10; id++) {
idizi[id] = 1;
printf("%d ", idizi[id]);
}
printf("\n");
if ((fp=fopen("deneme.txt", "rb")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
fread (idizi, sizeof (idizi), 1, fp);
for (id=0; id<10; id++) printf("%d ", idizi[id]);
fclose (fp);
return 0;
}
Program, bir önceki programın yaptığı işlem ile aynı işlemi gerçekleştirir. Tek fark dosyaya yazma ve dosyadan okuma işlemlerini bir defada yapmasıdır.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp1, *fp2;
unsigned char ucd;
if (argc!=3) {
printf("Kullanım : deneme <Kaynak dosya> <Hedef dosya>");
exit(1);
}
if ((fp1 = fopen(argv[1], "rb")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
if ((fp2 = fopen(argv[2], "wb")) == NULL) {
printf("Dosya açılamadı!\n");
exit(1);
}
while (!feof(fp1)) {
ucd = fgetc(fp1);
if (!feof(fp1)) fputc(~ucd, fp2);
}
fclose (fp1);
fclose (fp2);
return 0;
}
Program, ~ bit işlemcisini kullanarak, komut satırından verilen ilk dosyanın içeriğini kodlar ve elde edilen değerleri yine komut satırından argüman olarak verilen ikinci dosyaya kopyalar.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp1, *fp2;
unsigned char ucd;
if (argc!=4) {
printf("Kullanım : deneme <Kaynak dosya> <Hedef dosya> <Harf>");
exit(1);
}
if ((fp1 = fopen (argv[1], "rb")) == NULL) {
printf("Kaynak dosya açılamadı!");
exit(1);
}
if ((fp2 = fopen (argv[2], "wb")) == NULL) {
printf("Hedef dosya açılamadı!");
exit(1);
}
while (!feof(fp1)) {
ucd = fgetc(fp1);
ucd = *argv[3] ^ ucd;
if (!feof(fp1)) {
fputc(ucd, fp2);
}
}
fclose (fp1);
fclose (fp2);
if ((fp1 = fopen (argv[1], "wb")) == NULL) {
printf("Kaynak dosya açılamadı!");
exit(1);
}
if ((fp2 = fopen (argv[2], "rb")) == NULL) {
printf("Hedef dosya açılamadı!");
exit(1);
}
while (!feof(fp2)) {
ucd = fgetc(fp2);
ucd = *argv[3] ^ ucd;
if (!feof(fp2)) {
fputc(ucd, fp1);
}
}
fclose (fp1);
fclose (fp2);
return 0;
}
Program, ^ işlemcisini kullanarak komut satırından argüman olarak verilen dosyanın içeriğini yine komut satırından argüman olarak verilen bir karaktere göre kodlayarak komut satırından argüman olarak verilen dosyaya yazar. Sonra, kodlanmış dosyayı aynı işlemci ile açarak elde ettiği değerleri ilk dosyaya yazar.
Fonksiyon | Açıklama |
---|---|
void clearerr(FILE *stream) | Verilen akış için EOF ve hata göstergelerini siler. |
int fclose(FILE *stream) | Verilen skışı kapatır. Tüm tamponlar silinir. |
int feof(FILE *stream) | Verilen akış için EOF göstergesini kontrol eder. |
int ferror(FILE *stream) | Verilen akış için hata göstergesini kontrol eder. |
int fflush(FILE *stream) | Verilen akışın çıkış tampon belleğini sıfırlar. |
int fgetc(FILE *stream) | Verilen akış içinde, bir sonraki karakteri (unsigned char) okur ve konum göstergesini bir ileri taşır. |
int fgetpos(FILE *stream, fpos_t *pos) | Akışın aktif dosya konumunu alır ve pos değişkenine yazar. |
char *fgets(char *str, int n, FILE *stream) | Verilen akıştan bir satır okur ve str ile gösterilen karakter dizisine yazar. |
FILE *fopen(const char *filename, const char *mode) | Verilen dosya adını mode değerine uygun olarak açar. |
int fprintf(FILE *stream, const char *format, ...) | Yapılandırılmış çıkış değerini akışa gönderir. |
int fputc(int char, FILE *stream) | Verilen karakteri (unsigned char) akış içine yazar ve konum göstergesini bir ileri taşır. |
int fputs(const char *str, FILE *stream) | Verilen karakter dizisini, karakter dizisi sonunda bulunan NULL değer hariç, akışa yazar. |
int fscanf(FILE *stream, const char *format, ...) | Yapılandırılmış giriş değerini akıştan okur. |
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) | Verilen akıştan okuduğu değeri ptr ile gösterilen işaretçi adresine yazar. |
FILE *freopen(const char *filename, const char *mode, FILE *stream) | Yeni bir dosya adını verilen açık akışla ilişkilendirir ve aynı zamanda akıştaki eski dosyayı kapatır. |
int fseek(FILE *stream, long int offset, int whence) | Akışın dosya konumunu verilen değere ayarlar. Fonksiyona geçirilen offset parametresi, whence parametresi ile belirtilen konumdan itibaren aranacak bayt sayısını gösterir. |
int fsetpos(FILE *stream, const fpos_t *pos) | Verilen akışın dosya konumunu verilen konuma ayarlar. |
long int ftell(FILE *stream) | Verilen akışın geçerli dosya konumunu döndürür. |
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) | Fonksiyona geçirilen ptr adresindeki değeri verilen akışa yazar. |
int getc(FILE *stream) | Verilen akış içinde, bir sonraki karakteri (unsigned char) okur ve konum göstergesini bir ileri taşır. |
int putc(int char, FILE *stream) | Verilen karakteri (unsigned char) akış içine yazar ve konum göstergesini bir ileri taşır. |
int remove(const char *filename) | Adı verilen dosyayı siler. |
int rename(const char *old_filename, const char *new_filename) | Adı verilen dosyanın adını değiştirir. |
void rewind(FILE *stream) | Verilen akışın dosya konumunu dosyanın başına alır. |
void setbuf(FILE *stream, char *buffer) | Bir akışın nasıl tamponlanması gerektiğini tanımlar. |
int setvbuf(FILE *stream, char *buffer, int mode, size_t size) | Bir akışın nasıl tamponlanması gerektiğini tanımlar. |
FILE *tmpfile(void) | İkili sistem güncelleme modunda (wb+) geçici bir dosya açar. |
char *tmpnam(char *str) | Mevcut olmayan geçici bir dosya oluşturarak adını döndürür. |
int vfprintf(FILE *stream, const char *format, va_list arg) | Yapılandırılmış çıkışı bir argüman listesi kullanarak akışa gönderir. |
int ungetc(int char, FILE *stream) | Verilen karakteri (unsigned char) tanımlanan akışa geri gönderir. Böylece, bir sonraki karakter okunur. |