DOSYALAR
İkincil bellekte tanımlanmış bölgelere dosya denir. Her dosyanın bir ismi vardır. Ancak dosyaların isimlendirme kuralları sistemden sisteme göre değişebilmektedir. Dosya işlemleri tamamen işletim sisteminin kontrolü altındadır.
İşletim sistemi de ayrı ayrı yazılmış fonksiyonların birbirlerini çağırması biçiminde çalışır. Örneğin komut satırında ismi yazılmış olan bir programın çalıştırılması birkaç sistem fonksiyonunun çağırılması ile yapılmaktadır. Komut satırından yazıyı alan, diskte bir dosyayı arayan, bir dosyayı RAM’e yükleyen, RAM’deki programı çalıştıran fonksiyonlar düzenli olarak çağırılmaktadır.
İşletim sisteminin çalışması sırasında kendisinin de çağırdığı, sistem programcısının da dışarıdan çağırabildiği işletim sistemine ait fonksiyonlara sistem fonksiyonları denir. Bu tür fonksiyonlara Windows sisteminde API (Application Proggramming Interface) fonksiyonları, UNIX işletim sisteminde ise sistem çağırmaları (system calls) denir.
Aslında bütün dosya işlemleri, hangi programlama dili ile çalışılırsa çalışılsın, işletim sisteminin sistem fonksiyonları tarafından yapılır. Sistem fonksiyonlarının isimleri ve parametrik yapıları sistemden sisteme değişebilmektedir.
Dosyalara İlişkin Temel Kavramlar
Dosyanın Açılması
Bir dosya üzerinde işlem yapmadan önce dosya açılmalıdır. Dosya açabilmek için işletim sisteminin “dosya aç” isimli bir sistem fonksiyonu kullanılır. Dosyanın açılması sırasında dosya ile ilgili çeşitli ilk işlemler işletim sistemi tarafından yapılır.
Bir dosya açıldığında, dosya bilgileri, ismine “Dosya Tablosu” (File table) denilen ve işletim sisteminin içerisinde bulunan bir tabloya yazılır. Dosya tablosunun biçimi sistemden siteme değişebilir. Örneğin tipik bir dosya tablosu şağıdaki gibi olabilir :
Dosya tablosu (File table)
Sıra No
Dosya ismi
Dosyanın Diskteki Yeri
Dosyanın Özellikleri
Diğerleri
0
1
...
12
AUTOEXEC.BAT
...
...
...
...
Sistem fonksiyonlarının da parametreleri ve geri dönüş değerleri vardır. "Dosya aç" sistem fonksiyonunun parametresi açılacak dosyanın ismidir.Fonksiyon, dosya bilgilerinin yazıldığı sıra numarası ile geri döner ki bu değere "file handle" denir. Bu handle değeri diğer dosya fonksiyonlarına parametre olarak geçirilir. Dosyanın açılması sırasında buna ek olarak başka önemli işlemler de yapılmaktadır.
Dosyanın Kapatılması
Dosyanın kapatılması açılması sırasında yapılan işlemlerin geri alınmasını sağlar. Örneğin dosyanın kapatılması sırasında, işletim sisteminin dosya tablosunda bulunan bu dosyaya ilişkin bilgiler silinir. Açılan her dosya kapatılmalıdır. Kapatılmaması çeşitli problemlere yol açabilir.
Dosyaya Bilgi Yazılması ve Okunması
İşletim sistemlerinin dosyaya n byte veri yazan ve dosyadan n byte veri okuyan sistem fonksiyonları vardır. Yazma ve okuma işlemleri bu fonksiyonlar kullanılarak yapılır.
Dosya pozisyon göstericisi (file pointer)
Bir dosya byte’lardan oluşur. Dosyadaki her bir byte’a 0’dan başlayarak artan sırada bir sayı karşılık getirilmiştir. Bu sayıya ilgili byte’ın ofset numarası denir. Dosya pozisyon göstericisi long türden bir sayıdır ve bir ofset değeri belirtir. Dosyaya yazan ve dosyadan okuma yapan fonksiyonlar bu yazma ve okuma işlemlerini her zaman dosya pozisyon göstericisinin gösterdiği yerden yaparlar. Örneğin dosya göstericisinin gösterdiği yer 100 olsun. Dosyadan 10 byte bilgi okumak için sistem fonksiyonu çağırıldığında, 100. ofsetden itibaren 10 byte bilgi okunur. İşletim sisteminin dosya göstericisini konumlandıran bir sistem fonksiyonu vardır. Dosya ilk açıldığında dosya göstericisi 0. ofsetdedir. Örneğin bir dosyanın 100. Ofsetinden itibaren 10 byte okunmak istenirse sırası ile şu işlemlerin yapılması gerekir:
İlgili dosya açılır.
Dosya pozisyon göstericisi 100. Offsete konumlandırılır.
Dosyadan 10 byte okunur.
Dosya kapatılır.
C dilinde dosya işlemleri 2 biçimde yapılabilir :
1. İşletim sisteminin sistem fonksiyonları doğrudan çağırılarak.
2. Standart C fonksiyonları kullanılarak.
Prototipleri stdio.h içerisinde olan standart dosya fonksiyonlarının hepsinin ismi f ile başlar.Tabi standart C fonksiyonları da işlemlerini yapabilmek için aslında işletim sisteminin sistem fonksiyonlarını çağırmaktadır. İşletim sisteminin sistem fonksiyonları taşınabilir değildir. İsimleri ve parametrik yapıları sistemden sisteme değişebilir. Bu yüzden standart C fonksiyonlarının kullanılması tavsiye edilir.
fopen fonksiyonu
FILE *fopen (const char *fname, const char *mode)
Fonksiyonun 1. Parametresi açılacak dosyanın ismidir. 2. Parametre açış modudur. Dosya ismi path içerebilir. Dizin geçişleri için / de konulabilir. Açış modu şunlar olabilir :
Mode
Anlamı
"w"
Dosyayı yazmak için açar. Dosyadan okuma yapılamaz. Dosyanın mevcut olaması zorunlu değildir. Dosya mevcut değilse yaratılır. Dosya mevcut ise sıfırlanır.
"w+"
Dosyayı yazma ve okuma için açar. Dosyanın mevcut olaması zorunlu değildir. Dosya mevcut değilse yaratılır. Dosya mevcut ise sıfırlanır.
"r"
Dosyayı okuma için açar. Dosyaya yazma yapılamaz. Dosya mevcut değilse açılamaz.
"r+"
Dosyayı okuma ve yazma için açar. (Dosyanın pozisyon göstericisi dosyanın başındadır). Dosya mevcut değilse açılamaz.
"a"
Dosyayı sonuna ekleme için açar. Dosyadan okuma yapılamaz. Dosyanın mevcut olaması zorunlu değildir. Dosya mevcut değilse yaratılır.
"a+"
Dosyayı sonuna ekleme ve dosyadan okuma için açar. Dosyanın mevcut olaması zorunlu değildir. Dosya mevcut değilse yaratılır.
Fonksiyonun geri dönüş değerine ilişkin FILE yapısı stdio.h içerisinde bildirilmiştir. Fonskiyonun geri dönüş değeri FILE yapısı türünden bir adrestir. Bu yapının elemanları standart değildir. Sistemden sisteme değişiklik gösterebilir. Zaten programcı bu yapının elemanlarına gereksinim duymaz. fopen fonksiyonu işletim sisteminin dosya aç sistem fonksiyonunu çağırarak dosyayı açar ve dosyaya ilişkin bazı bilgileri bu FILE yapısı içerisine yazarak bu yapının başlangıç adresini geri döndürür. Örneğin "file handle" değeri de bu yapının içerisindedir. Tabi fopen fonksiyonunun geri verdiği FILE türünden adres güvenli bir adrestir. Dosya çeşitli sebeplerden dolayı açılamayabilir. Bu durumda fopen NULL gösterici döndürür. Fonksiyonun geri dönüş değeri kesinlikle kontrol edilmelidir. Tipik bir kontrol işlemi aşağıdaki gibi yapılabilir:
if ((f = fopen(“data”, “r”)) == NULL) {
printf(“cannot open file”...\n);
exit(1);}
Dosya ismi dosyanın yeri hakkında bilgi (sürücü, path gibi) içerebilir. Dosya ismi string ile veriliyorsa path bilgisi verirken dikkatli olmak gerekecektir. path bilgisi \ karakteri içerebilir. string içinde \ karakterinin kullanılması, \ karakterininin onu izleyen karakterle birlikte önceden belirlenmiş ters bölü karakter sabiti olarak yorunlanmasına yol açabilecektir. Örneğin :
fopen("C:\source\new.dat", "r");
fonksiyon çağırımında derleyici \n karakterini "newline" karakteri olarak yorumlayacak \s karakterini ise "undefined" kabul edecektir. Bu problemden sakınmak \ yerine \\ kullanılmasıyla mümkün olur:
fopen("C:\\source\\new.dat", "r");
Append modları ("a", "a+") çok kullanılan modlar değildir. Dosyaya yazma durumunda "w" modu ile "a" modu arasında farklılık vardır. "w" modunda dosyada olan ofsetin üzerine yazılabilir. "a" modunda ise dosya içeriği korunarak sadece dosyanın sonuna yazma işlemi yapılabilir. Bir dosyanın hem okuma hem de yazma amacıyla açılması durumunda (yani açış modunu belirten stringde '+' karakterinin kullanılması durumunda dikkatli olmak gerekir. Okuma ve yazma işlemleri arasında mutlaka ya dosya pozisyon göstericisinin konumlandırılması (mesela fseek fonksiyonu ile) ya da dosyaya ilişken tampon bellek alanının (buffer) tazelenmesi gerekecektir.
Bir dosyanın açılıp açılamayacağını aşağıdaki şekilde test edebiliri:
Program komut satırından
canopen dosya.dat
şeklinde çalıştırıldığında ekrana "xxxxx dosyası açılabilir" ya da "xxxxx dosyası açılamaz" yazacaktır.
#include
int main(int argc, char *argv[])
{
FILE *fp;
if (argc != 2) {
printf("usage: canopen filename\n");
return 2;
}
if ((fp = fopen(argv[1], "r")) == NULL) {
printf("%s dosyası açılamaz\n", argv[1]);
return 1;
}
printf("%s dosyası açılabilir\n", argv[1]);
fclose(fp);
return 0;
}
fclose fonksiyonu
int fclose(FILE *stream);
Bu fonksiyon açılmış olan bir dosyayı kapatır. Fonksiyon fopen ya da fropen fonksiyonundan elde edilen FILE yapısı türünden adresi parametre olarak alır ve açık olan dosyayı kapatır. Fonksiyonun geri dönüş değeri 0 ise dosya başarılı olarak kapatılmıştır. Fonksiyonun geri dönüş değeri EOF ise dosya kapatılamamıştır. EOF stdio.h içinde tanımlanan bir sembolik sabittir ve derleyicilerin çoğunda (-1) olarak tanımlanmıştır. Fonksiyonun başarısı ancak şüphe altında test edilmelidir. Normal şartlar altında dosyanın kapatılmaması için bir neden yoktur.
int main()
{
FILE *f;
if ((f = fopen(“data”, “w”)) ==NULL) {
printf(“cannot open file”...\n);
exit(1);
}
fclose(f);
return 0;
}
fgetc fonksiyonu
int fgetc(FILE *f);
İşletim sisteminin dolayısıyla C’nin yazma ve okuma yapan fonksiyonları yazılan ve okunan miktar kadar dosya göstericisini ilerletirler. fgetc fonksiyonu dosya göstericisinin gösterdiği yerdeki byte’ı okur ve geri dönüş değeri olarak verir. Fonksiyon başarısız olursa , stdio.h dosyası içerisinde sembolik sabit olarak tanımlanmış EOF değerine geri döner. Pek çok derleyici de EOF sembolik sabiti aşağıdaki gibi tanımlanmıştır.
#define EOF (-1)
fgetc fonksiyonunun geri dönüş değerini char türden bir değişkene atamak yanlış sonuç verebilir, bu konuda dikkatli olunmalı ve fonksiyonun geri dönüş değeri int türden bir değişkende saklanmalıdır.
char ch;
...
ch = fgetc(fp);
Yukarıda dosyadan okunan karakterin 255 numaralı ASCII karakteri (0x00FF) olduğunu düşünelim. Bu sayı char türden bir değişkene atandığında yüksek anlamlı byte'ı kaybedilecek ve ch değişkenine 0xFF değeri atanacaktır. Bu durumda ch değişkeni işaretli char türden olduğundan ch değişkeni içinde negatif bir sayının tutulduğu anlamı çıkar.
if (ch == EOF)
gibi bir karşılaştırma deyiminde, if parantezi içerisindeki karşılatırma işleminin yapılabilmesi için otomatik tür dönüşümü yapılır. Bu otomatik tür dönüşümünde işaretli int türüne çevrilecek ch değişkeni FF byte'ı ile beslenecektir. (negatif olduğu için). Bu durumda eşitlik karşılaştırması doğru netice verecek, yani dosyanın sonuna gelindiği (ya da başka nedenden dolayı okumanın yapılamadığı) yorumu yapılacaktır.
Oysa ch değişkeni int türden olsaydı, ch değişkenine atanan değer 0x00FF olacak ve bu durumda karşılaştırma yapıldığında ch değişkeni ile EOF değerinin (0xFFFF) eşit olmadığı sonucuna varılacaktır.
Bir dosyanın içeriğini ekrana yazdıran örnek program:
#include
int main()
{
FILE *f;
char fname[MAX_PATH];
int ch;
printf(“Dosya ismi : “);
gets(fname);
if (f = fopen(fname, “r”)) == NULL) {
printf(“cannot open file...\n”);
exit(1);
}
while (ch = fgetc(f)) != EOF)
putchar(ch);
fclose(f);
return 0;
}
not : maximum path uzunluğu DOS’da 80 UNIX ve WINDOWS’da 256 karakteri geçemez.
fputc Fonksiyonu
int fputc(int ch, FILE *p);
Bu fonksiyon dosya göstericisinin bulunduğu yere 1 byte bilgiyi yazar. Fonksiyonun 1. parametresi yazılacak karakter, 2. parametresi ise yazılacak dosyaya ilişkin FILE yapısı adresidir. Fonksiyonun geri dönüş değeri EOF ise işlem başarısızdır. Değilse fonksiyon yazılan karakter ile geri döner.
fgetc ve fputc fonksiyonları kullanılarak bir dosyayı kopyalayan örnek program:
#include
#include
#define MAX_PATH 80
int main()
{
FILE *fsource, *fdest;
char source[MAX_PATH], dest[MAX_PATH];
int ch;
printf(“kopyalanacak dosya : “);
gets(source);
printf(“kopya dosya : “);
gets (dest);
if ((fsource = fopen(source, “r”)) == NULL) {
printf(“cannot open file...\n”);
exit(EXIT_FAILURE);
}
if ((fdest = fopen(dest, “w”)) == NULL) {
printf(“cannot create file...\n”);
exit(EXIT_FAILURE);
}
while ((ch = fgetc(fsource)) != EOF)
fputc(ch, fdest);
fclose(fsource);
fclose(fdest);
printf(“1 file copied...\n”);
return 0;
}
fprintf Fonksiyonu
Bu fonksiyon tıpkı printf fonksiyonu gibidir. Ancak ilk parametresi yazma işleminin hangi dosyaya yapılacağını belirtir. Diğer parametreleri printf fonksiyonun da olduğu gibidir. Özetle printf fonksiyonu ekrana yazar ancak fprint fonksiyonu 1. parametre değişkeninde belirtilen dosyaya yazar.
#include
#include
#define MAX_PATH 80
int main()
{
FILE *f;
int i;
if ((f = fopen(“data”, “w”)) ==NULL) {
printf(“cannot open file...\n");
exit(1);
}
for (i = 0; i < 10; ++i)
fprintf(f, “sayi = %d\n”, i);
fclose(f);
return 0;
}
fgets fonksiyonu
char *fgets(char *buf, int n, FILE *f);
Bu fonksiyon dosya göstericisinin gösterdiği yerden 1 satırlık bilgiyi okur. Fonksiyon dosyadan '\n' karakterini okuyunca onu da birinci parametresinde verilen adrese yazarak işlemini sonlandırır.
Fonksiyonun 1. parametresi okunacak bilginin RAM'de yerleştirileceği yerin adresidir. 2. parametresi ise okunacak maksimum karakter sayısıdır. fgets en fazla n –1 karakteri okur. Okuduğu karakterlerin soonuna null karakteri ekler ve işlemini sonlandırır. Eğer satır üzerindeki karakter sayısı n- 1'den az ise tüm satırı okur ve işlemini sonlandırır. Örneğin bu parametreyi 10 olarak girdiğimizi düşünelim. Satır üzerinde 20 karakter olsun. Fonksiyon 9 karakteri okur, sonuna NULL karakteri ekler. Ancak satır üzerinde \n dahil olmak üzere 5 karakter olsaydı fonksiyon bu 5 karakteri de okuyarak sonuna da NULL karakter ekleyerek işlemini sonlandıracaktı. Bir döngü içerisinde fgets fonksiyonu sürekli olarak çağırılarak bütün dosya okunabilir.
Fonksiyonun geri dönüş değeri, en az 1 karakter okunmuş ise 1. parametresi ile belirtilen adresin aynısı, hiç okunmamışsa NULL adresidir.
Örnek : Bir dosyayı fgets fonksiyonu ile satır satır olarak ekrana yazan program:
#define MAX_PATH 80
#define MBUFSIZE 100
int main()
{
FILE *f;
char s[MAX_PATH];
char buf[BUFSIZE];
printf(“Dosya : “);
gets(s);
if ((f = fopen(s, “r”)) == NULL) {
printf(“cannot open file...\n”);
exit(1);
}
while (fgets(buf, BUFSIZE, f) != NULL)
printf(“%s”, buf);
fclose(f);
return 0;
}
Text ve Binary Dosya Kavramları
DOS VE WINDOWS işletim sistemlerinde bir dosya text ve binary modda açılabilir. Varsayılan açış modu textdir. Yani dosyanın hangi modda açıldığı açık bir şekilde belirtilmezse dosyanın text modda açıldığı varsayılacaktır. Dosyayı binary modda açabilmek için açış modunun sonuna b eklemek gerekir. Örneğin :
f = fopen(“data”, “r”) text modda
f = fopen(“data, “rb”) binary modda
DOS’da bir dosya type edildiğinde bir karakter aşağı satırın başında görünüyorsa bunu sağlamak için o karakterden önce CR (carriage return) ve LF (line feed) karakterlerinin bulunması gerekir. CR karakteri C’de ‘\r’ ile belirtilir. 13 numaralı ASCII karakteridir. LF karakteri C’de \n ile belirtilir. 10 numaralı ASCII karakteridir. Örneğin bir dosya type edildiğinde görüntü
a
b
şeklinde olsun. Dosyadaki durum a\r\nb şeklindedir. Oysa UNIX sistemlerinde aşağı satırın başına geçebilmek için sadece LF karakteri kullanılmaktadır. UNIX de
a
b
görüntüsünün dosya karşılığı
a\nb biçimindedir.
DOS’da LF karakteri bulunulan satırın aşağısına geç CR karakteri ise bulunulan satırın başına geç anlamındadır. Örneğin DOS da bir bir dosyanın içeriği a\nb biçiminde ise dosya type edildiğinde
a
b
görüntüsü elde edilir. Eğer dosyanın içeriği a\rb biçiminde ise dosya type edildiğinde
b
görüntüsü elde edilir.
printf fonksiyonunda \n ekranda aşağı satırın başına geçme amacıyla kullanılır. Aslında biz printf fonksiyonunun 1. parametresi olan stringin içine \n yerleştirdiğimizde UNIX'de yalnızca \ni DOS'da ise \r ve\n ile bu geçiş sağlanır.
Text dosyaları ile rahat çalışabilmek için dosyalar text ve binary olarak ikiye ayrılmıştır. Bir dosya text modunda açıldığında dosyaya \n karakteri yazılmak istendiğinde dosya fonksiyonları otomatik olarak \r ve \n karakterlerini dosyaya yazarlar. Benze r bir biçimde dosya text modda açılmışsa dosya göstericisi \r\n çiftini gösteriyorsa dosyadan yalnızca /n karakteri okunur. DOS işletim sisteminde text ve binary dosyalar arasındaki başka bir fark da, CTRL Z (26 numaralı ASCII karakterinin) dosyayı sonlandırdığının varsayılmasıdır. Oysa dosya binary modda açıldığında böyle bir varsayım yapılmaz.
UNIX işletim sisteminde text modu ile binary mod arasında hiçbir fark yoktur. Yani UNIX işletim sisteminde dosyanın binary mod yerine text modunda açılmasının bir sakıncası olmayacaktır. Ancak DOS altında text dosyası olmayan bir dosyanın binary mod yerine text modunda açılmasının sakıncaları olabilir. Örneğin DOS altında bir exe dosyanın binary mod yerine text modda açıldığını düşünelim. Bu dosyada 10 numaralı ve 13 numaralı ASCII karakterleri yanyana bulunduğunda dosyadan yalnızca 1 byte okunacaktır. Aynı şekilde dosyadan 26 numaralı ASCII karakteri okunduğunda dosyadan artık başka bir okuma yapılamayacaktır. (Dosyanın sonuna gelindiği varsayılacaktır.)
/*
textmode.c programı
DOS altında text modu ile binary mod arasındaki
farkı gösteren bir program
*/
#include
#include
#include
main()
{
FILE *fp;
int k, ch;
clrscr();
fp = fopen("deneme", "w");
if (fp == NULL) {
printf("dosya açılamıyor\n");
exit(EXIT_FAILURE);
}
/* dosyaya 5 tane \n karakteri yazdırılıyor */
for (k = 0; k < 5; ++k)
fputc('\n', fp);
fclose(fp);
printf("\ndosya binary modda açılarak yazdırılıyor\n");
fp = fopen("deneme", "rb");
if (fp == NULL) {
printf("dosya açılamıyor\n");
exit(EXIT_FAILURE);
}
while ((ch = fgetc(fp)) != EOF)
printf("%d ", ch);
/* ekran çıktısı
13 10 13 10 13 10 13 10 13 10
*/
fclose(fp);
printf("\ndosya kapatıldı. Şimdi dosya text modunda açılarak yazdırılıyor .\n");
fp = fopen("deneme", "r");
if (fp == NULL) {
printf("dosya açılamıyor\n");
exit(EXIT_FAILURE);
}
while ((ch = fgetc(fp)) != EOF)
printf("%d ", ch);
/* ekran çıktısı
10 10 10 10 10
*/
fclose(fp);
/* simdi '\x1A' karakterinin text modunda dosyayı sonlandırması özelliği
test ediliyor */
fp = fopen("deneme", "w");
if (fp == NULL) {
printf("dosya açılamıyor\n");
exit(EXIT_FAILURE);
}
/* dosyaya 5 tane 'A' karakteri yazdırılıyor */
for (k = 0; k < 5; ++k)
fputc('A', fp);
/* dosyaya '\x1A' karakteri yazdırılıyor */
fputc('\x1a', fp);
/* dosyaya 10 tane 'A' karakteri yazdırılıyor. */
for (k = 0; k < 5; ++k)
fputc('A', fp);
fclose(fp);
printf("\ndosya binary modda açılarak yazdırılıyor :\n");
fp = fopen("deneme", "rb");
if (fp == NULL) {
printf("dosya açılamıyor\n");
exit(EXIT_FAILURE);
}
while ((ch = fgetc(fp)) != EOF)
printf("%d ", ch);
/* ekran çıktısı
65 65 65 65 65 26 65 65 65 65 65
*/
printf("\ndosya kapatıldı, Şimdi dosya text modunda açılarak yazdırılıyor\n");
fp = fopen("deneme", "r");
if (fp == NULL) {
printf("dosya açılamıyor\n");
exit(EXIT_FAILURE);
}
while ((ch = fgetc(fp)) != EOF)
printf("%d ", ch);
/* ekran çıktısı
65 65 65 65 65 26 65 65 65 65 65
*/
fclose(fp);
return 0;
}
EOF (END OF FILE) DURUMU
Dosyanın sonunda hiçbir özel karakter yoktur. İşletim sistemi dosyanın sonuna gelinip gelinmediğini dosyanın uzunluğuna bakarak anlayabilir. EOF durumu dosya pozisyon göstericisinin dosyada olmayan son karakteri göstermesi durumudur. EOF durumunda dosya pozisyon göstericisinin offset değeri dosya uzunluğu ile aynı değerdedir. EOF durumunda dosyadan okuma yapılmak istenirse dosya fonksiyonları başarısız olur. Ancak açış modu uygunsa dosyaya yazma yapılabilir ve bu durumda dosyaya ekleme yapılır.
Daha önce söylendiği gibi C dilinde açılan bir dosya ile ilgili bilgiler FILE türünden bir yapı nesnesi içinde tutulur. Bu yapının her bir elemanı dosyanın bir özelliği hakkında bilgi vermektedir. C programcısı bu yapının elemanlarının değerleri ile doğrudan ilgilenmez, zira fopen fonksiyonunun geri dönüş değeri bu yapı nesnesini gösteren FILE yapısı türünden bir göstericidir ve C dilinin dosyalarla ilgili işlem yapan fonksiyonları çoğunlukla bu adresi parametre olarak alarak, istenilen dosyaya ulaşırlar.
Söz konusu FILE yapısının elemanlarından biri de flag olarak kullanılan EOF indikatörüdür. (aslında int türden bir flag'in yalnızca belirli bir bitidir.) C dilinin dosyalarla ilgili işlem yapan bazı fonksiyonları EOF indikatörünün değerini değiştirirler. (set ya da clear ederler. Set edilmesi bu indikatöre 1 değerinin atanması clear edilmesi ise 0 değerinin atanmasıdır. Bu indikatörün set edilmesi demek dosya pozisyon göstericisinin dosyanın sonunu göstermesi demektir.
Dosya açan fonksiyonlar FILE yapısındaki EOF indikatörünü sıfırlarlar. Bu fonksiyonlar dışında fseek fonksiyonu ve clearerr fonksiyonları da EOF indikatörünü sıfırlarlar. (clear ederler).
fseek fonksiyonu :
Bu fonksiyon dosya pozisyon göstericisini istenilen bir offsete konumlandırmak amacıyla kullanılır. Bu fonksiyonun kullanılmasıyla, açılmış bir dosyanın istenilen bir yerinden okuma yapmak ya da istenilen bir yerine yazmak mümkün hale gelir. Prototipi :
int fseek(FILE *f, long offset, int origin);
Fonksiyonun ikinci parametresi konumlandırma işleminin yapılacağı offset değeridir.
Fonksiyonun 3. parametresi 0, 1, veya 2 olabilir. Bu değerler stdio.h dosyasında
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
biçiminde sembolik sabitlerle tanımlanmıştır ve fseek fonksiyonun çağırılmasında daha çok bu sembolik sabitler kullanılmaktadır.
son parametre 0 ise, konumlandırma dosya başından itibaren yapılır. Bu durumda 2. parametre >= 0 olmalıdır. Örneğin:
fseek(f, 10L, 0);
ile dosya göstericisi 10. offsete konumlandırılır. Ya da
fseek(f, 0, 0);
ile dosya göstericisi dosyanın başına konumlandırılır. Dosya pozisyon göstericisinin dosyanın başına konumlandırılması için rewind fonksiyonu da kullanılabilir:
void rewind(FILE *fp);
rewind(f);
fonksiyonun 3. parametre değişkenine geçilen değer 1 ise (SEEK_CUR) , konumlandırma dosya göstericisinin en son bulunduğu yere göre yapılır. Bu durumda ikinci parametre pozitif ya da negatif değere sahip olabilir. Pozitif bir değer ileri, negatif bir değer geri anlamına gelecektir. Örneğin dosya göstericisi 10. offsette olsun.
fseek(f, -1, SEEK_CUR);
çağırması ile dosya göstericisi 9. offset'e konumlandırılır.
fonksiyonun 3. parametre değişkenine geçilen değer 2 ise (SEEK_END), konumlandırma EOF durumundan itibaren yani dosya sonunu referans alınarak yapılır. Bu durumda ikinci parametre <= 0 olmalıdır. Örneğin dosya göstericisini EOF durumuna çekmek için :
fseek(f, 0, SEEK_END);
çağırmasını yapmak gerekir. Ya da başka bir örnek:
fseek(f, -1, SEEK_END);
çağırması ile dosya göstericisi son karakterin offsetine çekilir. Fonksiyonun geri dönüş değeri işlemin başarısı hakkında bilgi verir. Geri dönüş değeri 0 ise işlem başarılıdır. Geri dönüş değeri 0 dışı bir değer ise işlem başarısızdır. Ancak problemli durumlarda geri dönüş değrinin test edilmesi tavsiye edilir.
Yazma ve okuma işlemleri arasında dosya göstericisinin fseek fonksiyonu ile konumlandırılması gerekir.Konumlandırma gerekirse boş bir fseek çağırması ile yapılabilir. Örneğin dosyadan bir karakter okunup , bir sonraki karaktere bu karakterin 1 fazlasını yazacak olalım.
ch = fgetc(f);
fputc(ch + 1, f);
işlemi hatalıdır. Yazmadan okumaya, okumadan yazmaya geçişte dosya göstericisi konumlandırılmalıdır.
feof fonskiyonu :
bu fonksiyon dosya göstericisinin EOF durumunda olup olmadığını (EOF indikatörünün set edilip edilmediğini) test etmek amacıyla kullanılır. Prototipi:
int feof(FILE *f);
Eğer dosya göstericisi dosya sonunda ise (EOF indikatörü set edilmişse) fonksiyon 0 dışı bir değere , değilse (EOF indikatörü set edilmemişse) 0 değeri ile geri döner.
Ancak feof fonksiyonunun 0 dışı bir değere geri dönebilmesi için dosya göstericisinin dosyanın sonunda olmasının yanı sıra en son yapılan okuma işleminin de başarısız olması gerekir. daha önce söylendiği gibi bazı fonksiyonlar (fopen, fseek, rewind, clearerr) EOF indikatorünü clear ederler, yani EOF indikatörünün 0 değerinde olması (clear edilmiş olması) dosyanın sonunda olunmadığının bir garantisi değildir.
fread ve fwrite fonksiyonları
Bu iki fonksiyon C dilinde en çok kullanılan dosya fonksiyonlarıdır. Genel olarak dosya ile RAM arasında transfer yaparlar. Her iki fonskiyonun da prototipleri aynıdır.
size_t fread(void *adr, size_t size, size_t n, FILE *);
size_t fwrite (const void *adr, size_t size, size_t n, FILE *);
size_t türünün derleyiciyi yazanlar tarafından unsigned int ya da unsigned long türünün typedef edilmiş yeni ismi olduğunu hatırlayalım.
fread fonskiyonu dosya pozisyon göstericisinin gösterdiği yerden, 2. ve 3. parametresine kopyalanan değerlerin çarpımı kadar byte'ı , RAM'de 1. parametresinin gösterdiği adresten başlayarak kopyalar. Geleneksel olarak fonksiyonun 2. parametresi veri yapısının bir elemanının uzunluğunu, 3. parametresi ile parça sayısı biçiminde girilir.
Bu fonksiyonlar sayesinde diziler ve yapılar tek hamlede dosyaya transfer edilebilirler. Örneğin 10 elemanlı bir dizi aşağıdaki gibi tek hamlede dosyaya yazılabilir.
int a[5] = {3, 4, 5, 7, 8};
fwrite (a, sizeof(int), 5, f);
Yukarıdaki örnekte, dizi ismi olan a int türden bir adres bilgisi olduğu için, fwrite fonksiyonuna 1. arguman olarak gönderilebilir. FILE türünden f göstericisi ile ilişkilendirilen dosyaya RAM'deki a adresinden toplam sizeof(int) * 5 byte yazılmaktadır.
Ancak tabi fwrite fonskiyonu sayıları bellekteki görüntüsü ile dosyaya yazar. (yani fprintf fonksiyonu gibi formatlı yazmaz.) Örneğin:
int i = 1535;
fwrite(&i, sizeof(int), 1, f);
Burada dosya type edilirse 2 byte uzunluğunda rasgele karakterler görünür. Çünkü DOS'da int türü 2 byte uzunluğundadır. Bizim gördüğümüz ise 1525'in rasgele olan bytelarıdır. Bilgileri ASCII karşılıkları ile dosyaya yazmak için fprintf fonksiyonu kullanılabilir..
fread ve fwrite fonksiyonları bellekteki bilgileri transfer ettiğine göre dosyaların da binary modda açılmış olması uygun olacaktır.
#include
#include
int main()
{
FILE *f;
int i;
int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int b[10];
if ((f = fopen("data", w+b")) == NULL) {
printf("cannot open file...\n");
exit(EXIT_FAILURE);
}
fwrite (a, sizeof(int), 10, f);
fseek(f, 0, SEEK_SET);
fread(b, sizeof(int), 10, f);
for (i = 0; i < 10; ++i)
printf("%d\n", b[i]);
}
fread ve fwrite fonksiyonlarının geri dönüş değerleri 3. parametresi ile belirtilen okunan ya da yazılan parça sayısıdır. Örneğin
n = fread(a, sizeof(int), 10, f);
ifadesinde fonksiyon bütün sayıları okuyabildiyse 10 sayısına geri döner. Eğer dosyadaki kalan byte sayısı okunmak istenen sayıdan az ise fonksiyon bütün byte'ları okur ve geri dönüş değeri okunan byte sayısı 2. parametresi ile belirtilen sayı olur. Örneğin DOS altında çalışıyor olalım. Dosyada 10 byte bilgi kalmış olsun.
n = fread(a, sizeof(int), 10, f);
ile fonksiyon 5 sayısına geri dönecektir.
Aşağıdaki iki ifadeyi inceleyelim:
fread(str, 100, 1, f);
fread(str, 1, 100, f);
her iki fonksiyon çağırma ifadesi de RAM'deki str adresine FILE türünden f göstericisi ile ilişkilendirilen dosyadan 100 byte okumak amacıyla kullanılabilir. Ancak birinci çağırmada geri dönüş değeri ya 0 ya 1 olabilecekken, ikinci fonksiyon çağırmasında geri dönüş değeri 0 100(dahil) herhangi bir değer olabilecektir.
Blok blok kopyalama işlemi
Aşağıdaki örnekte bir grup byte fread fonskiyonu ile bir dosyadan okunmuş ve fwrite fonksiyonu ile diğer bir dosyaya yazılmıştır.
#define BLOCK_SIZE 1024
#define MAX_PATH 80
int main()
{
FILE *fs, *fd;
char s[MAX_PATH], d[MAX_PATH];
unsigned n;
printf("kaynak dosya : ");
gets(s);
printf("amaç dosya : ");
gets(d);
if ((fs = fopen(s, "rb")) == NULL) {
printf("cannot open file ...\n");
exit(EXIT_FAILURE);
}
if ((fd = fopen(d, "wb")) == NULL) {
printf("cannot open file ...\n");
fclose(fs);
exit(EXIT_FAILURE);
}
while ((n = fread(buf, 1, BLOCK_SIZE, fs)) > 0)
fwrite(buf, 1, n, fd);
fclose(fs);
fclose(fd);
printf("1 file copied...\n");
return 0;
}
remove fonksiyonu
Bu fonksiyon bir dosyayı silmek için kullanılır. Fonksiyonun prototipi :
int remove (const char *filename);
şeklindedir. Fonksiyona arguman olarak silinecek dosyanın ismi gönderilir. Fonksiyonun geri dönüş değeri, dosyanın başarılı bir şekilde silinebilmesi durumunda 0 aksi halde (yani dosya silinememişse) 0 dışı bir değerdir. Açık olan bir dosyanın silinmesi "implementation depended" (derleyiciye bağlı) olduğundan, yazılan kodun taşınabilirliği açısından, silinecek bir dosya açık ise önce kapatılmalıdır.
rename fonksiyonu
Bu fonksiyon bir dosyanın ismini değiştirmek için kullanılır. Fonksiyonun prototipi :
int rename (const char *old, const char *new);
şeklindedir.
Fonksiyona 1. arguman olarak dosyanın eski ismi ikinci arguman olarak ise dosyanın yeni ismi gönderilmelidir. Fonksiyonun geri dönüş değeri, isim değiştirmen işleminin başarılı olması durumunda 0, aksi halde (dosyanın ismi değiştirilemiyorsa 0 dışı bir değerdir. (Örneğin açık olan bir dosyanın isminin değiştirilmeye çalışılması başarılı olamayacağından fonksiyon bu durumda 0 dışı bir değere geri dönecektir.)
tmpfile fonksiyonu
Fonksiyon geçici bir dosya açmak amacıyla kullanılır. Fonksiyonun prototipi :
FILE * tmpfile(void);
tmpfile fonksiyonu açtığı geçici dosyayı "wb" modunda açar. Açılan dosya fclose fonksiyonu ile kapatıldığında ya da (kapatılmazsa), program sona erdiğinde otomatik olarak silinecektir. Fonksiyonu geri dönüş değeri, açılan geçici dosya ile ilişki kurulmasına yarayacak, FILE yapısı türünden bir adrestir. Herhangi bir nedenle dosya geçici dosya açılamıyorsa fonksiyon NULL adresine geri dönecektir.
tmpnam fonksiyonu
Geçici olarak kullanılacak bir dosya için bir isim üretilmesi amacıyla kullanılır. Fonksiyonun prototipi :
char *tmpnam(char *s);
şeklindedir. Fonksiyon ürettiği dosya ismini kendisine gönderilen char türden adrese yerleştirir. Eğer fonksiyona arguman olarak NULL adresi gönderilirse, fonksiyon üretilen dosya ismini statik bir dizi içinde tutarak bu dizinin adresiyle geri dönecektir. Fonksiyona char türden bir dizinin adresi gönderildiğinde bu dizinin boyutu ne kadar olmalıdır. Başka bir deyişle tmpnam fonksiyonu kaç karakter uzunluğunda bir dosya ismi üretecektir. İşte bu değer stdio.h dosyası içinde tanımlanan L_tmpnam sembolik sabitiyle belirtilmektedir.
tmpnam fonksiyonunun ürettiği dosya isminin daha önce kullanılmayan bir dosya ismi olması garanti altına alınmıştır. Yani üretilen dosya ismi tektir. (unique file name) Bir programda daha sonra silmek üzere bir dosya açacağımızı ve bu dosyaya birtakım bilgileri yazacağımız düşünelim. Bu durumda dosyayı yazma modunda açacağımıza göre, açacağımız dosyaya olan bir dosyanın ismini veremeyiz. Eğer verirsek , var olan dosya sıfırlanacağı için bu dosyayı kaybederiz. Bu riske girmemek için, geçici olarak kullanılacak dosya tmpfiel fonksiyonu kullanılarak açılmalıdır. Ancak tmpfile fonksiyonunun kullanılması durumunda, açılan dosya kalıcı hale getirilemez. Yani herhangi bir nedenden dolayı geçici dosyanın silinmemesini istersek, (dosyayı kalıcı hale getirmek istersek) dosyayı fopen fonksiyonuyla açmalıyız. İşte bu durumda geçici dosyayı başka bir dosyayı riske etmemek için tmpnam fonksiyonunun ürettiği isim ile açmalıyız. Peki tmpnam fonksiyonuyla en fazla kaç tane "unique file name " üretebiliriz. İşte bu sayı stdio.h içinde tanımlanan TMP_MAX sembolik sabiti ile belirlenmiştir.
C dilinde bazı giriş ve çıkış birimleri(klavye, ekran gibi) doğrudan bir dosya gibi ele alınırlar. C standartları herhangi bir giriş çıkış birimini "stream" olarak isimlendirmektedir. Bir stream bir dosya olabileceği gibi, dosya olarak ele alınan bir giriş çıkış birimi de olabilir. Örneğin küçük programlar genellikle girdilerini genellikle tek bir stream,den alıp (mesela klavye) çıktılarını da tek bir streame (mesela ekran) iletirler.
freopen fonksiyonu
freopen fonksiyonu daha önce açılmış bir dosyayı, fropen fonksiyonu ile açılan dosyaya yönlendirir. Fonksiyonun prototipi :
FILE *freopen(const char *filename, const char *mode, FILE *stream);
şeklindedir.
Uygulamalarda daha çok standart dosyaların (stdin, stdout, stderr) başka dosyalara yönlendirilmesinde kullanılır. Örneğin bir programın çıktılarının data.dat isimli dosyaya yazılmasını istersek :
if (freopen("data.dat", "w", stdout) == NULL) {
printf("data.dat dosyası açılamıyor\n");
exit(EXIT_FAILURE);}
yukarıdaki fonksiyon çağırımıyla stdout dosyasının yönlendirildiği dosya kapatılarak (bu yönlendirme işlemi komut satırından yapılmış olabileceği gibi, freopen fonksiyonunun daha önceki çağırımı ile de yapılmış olabilir.) stdout dosyasının data.dat dosyasına yönlendirilmesi sağlanır.
freopen fonksiyonunun geri dönüş değeri fonksiyona gönderilen üçüncü arguman olan FILE yapısı türünden göstericidir. freopen dosyası yönlendirmenin yapılacağı dosyayı açamazsa NULL adresine geri döner. Eğer yönlendirmenin yapıldığı eski dosya kapatılamıyorsa, freopen fonksiyonu bu durumda bir işaret vermez.
dosya buffer fonksiyonları
İkincil belleklerle (disket, hard disk vs.) yapılan işlemler RAM'de yapılan işlemlere göre çok yavaştır. Bu yüzden bir dosyadan bir karakterin okunması ya da bir dosyaya bir karakterin yazılması durumunda her defasında dosyaya doğrudan ulaşmak verimli bir yöntem değildir.
İşlemin performansı bufferlama yoluyla artırılmaktadır. Bir dosyaya yazılacak data ilk önce bellekteki bir buffer alanında saklanır. Bu buffer alanı dolduğunda ya da yazılmanın yapılacağı dosya kapatıldığında bufferdaki data alanında ne varsa dosyaya yazılır. Buna bufferin boşaltılması (to flush the buffer) denir.
Giriş dosyaları da benzer şekilde bufferlanabilir. Giriş biriminden alınan data (örneğin klavyeden) önce buffera yazılır.
dosyaların bufferlaması erimlilikte çok büyük bir artışa neden olur. Çünkü bufferdan (RAM'den) bir karakter okunması ya da buffera bir karakter yazılması ihmal edilecek kadar küçük bir zaman içinde yapılır. Buffer ile dosya arasındaki transfer şüphesiz yine vakit alacaktır ama bir defalık blok transferi, küçük küçük transferlerin toplamından çok daha kısa zaman alacaktır.
stdio.h başlık dosyası içinde prototipi bildirimi yapılan ve dosyalarla ilgili işlem yapan fonksiyonlar tamponlamayı otomatik olarak gerçekleştirirler. Yani dosyaların tamponlanması için bizim birşey yapmamıza gerek kalmadan bu iş geri planda bize sezdirilmeden yapılmaktadır. Ama bazı durumlarda tamponlama konusunda programcı belirleyici durumda olmak isteyebilir. İşte bu durumlarda programcı dosya tamponlama fonksiyonlarını (fflush, setbuf, setvbuf) kullanacaktır:
fflush fonksiyonu
Bir program çıktısını bir dosyaya yazarken (örneğin stdout dosyasına) yazılan dosya ilk önce RAM'deki tamponlama alanına gider. Dosya kapatıldığında ya da tamponlama alanı dolduğunda, tamponlama alanı boşaltılarak dosyaya yazılır. fflush fonksiyonunun kullanılmasıyla, dosyanın kapatılması ya da tamponlama alanının dolması beklenmeksizin, tamponlama alanı boşatılarak dosyaya yazılır. Bu işlem istenilen sıklıkta yapılabilir. Fonksiyonun prototipi :
int fflush (FILE *stream);
şeklindedir.
fflush(fp);
çağırımı ile FILE yapısı türünden fp göstericisi ile ilişkilendirilen dosyanın tamponlama alanı (buffer) boşaltılır. Eğer fflush fonksiyonuna NULL adresi gönderilirse, açık olan bütün dosyaların tamponlama alanları boşaltılacaktır.
Tamponlama alanının boşaltılması işlemi başarılı olursa fflush fonksiyonu 0 değerine geri dönecek aksi halde EOF değerine geri dönecektir.
setvbuf fonksiyonu
setvbuf fonksiyonu bir dosyanın tamponlanma şeklinin değiştirilmesi ve tampon alanının yerinin ve boyutunun değiştirilmesi amacıyla kullanılır. Fonksiyonun prototipi aşağıdaki şekildedir :
int setvbuf(FILE *stream, char *buf, int mode, size_t size);
Fonksiyona gönderilen üçüncü arguman tamponlama şeklini belirler. Üçüncü argumanın değeri stdio.h başlık dosyası içinde tanımlanan sembolik sabitlerle belirlenir.
_IOFBF (full buffering - tam tamponlama)
data dosyaya tamponlama alanı dolduğunda yazılır. Ya da giriş tamponlaması söz konusu ise dosyadan okuma tamponlama alanı boş olduğu zaman yapılır.
_IOLBF (line buffering - satır tamponlaması)
Tamponlama alanı ile dosya arasındaki okuma ya da yazma işlemi satır satır yapılır.
_IONBF (no buffering - tamponlama yok)
Dosyadan okuma ya da soyaya yazma tamponlama olmadan doğrudan yapılır.
setvbuf fonksiyonuna gönderilen ikinci arguman RAM'de tamponlamanın yapılacağı bloğun başlangıç adresidir. Tamponlamanın yapılacağı alan statik ya da dinamik ömürlü olabileceği gibi, dinamik bellek fonksiyonlarıyla da tahsis edilebilir.
Fonksiyona gönderilen son arguman tamponlama alanında tutulacak byteların sayısıdır.
setvbuf fonksiyonu dosya açıldıktan sonra, fakat dosya üzerinde herhangi biri işlem yapılmadan önce çağırılmalıdır. Fonksiyonun başarılı olması durumunda fonksiyon 0 değerine geri dönecektir. Fonksiyona gönderilen üçüncü argumanın geçersiz olması durumunda ya da fonksiyonun ilgili tamponlamayı yapamaması durumunda, geri dönüş değeri 0 dışı bir değer olacaktır.
Fonksiyona gönderilen buffer alanının geçerliliğinin bitmesinden önce (ömrünün tamamlanmasından önce) dosya kapatılmamalıdır.
dosya işlemleri ile ilgili örnek uygulamalar:
Uygulama 1 : Dosya üzerinde sıralı veri tabanı tutulması
#include
#include
#include
#include
/* symbolic constants */
#define MAX_PATH 80
#define ADDREC 1
#define LISTREC 2
#define FINDREC 3
#define DELREC 4
#define EDITREC 5
#define PACKRECS 6
#define SORTREC 7
#define EXITPROG 8
#define DELETED 0
#define NORMALREC 1
#define INVALID 0
/* structure declarations */
typedef struct _PERSON {
char name[30];
int no;
int delflag;
} PERSON;
/* function prototypes */
int GetOption(void);
void AddRec(void);
void ListRec(void);
void FindRec(void);
void DelRec(void);
void EditRec(void);
void SortRec(void);
void Pack(void);
/* global variables */
FILE *f;
char fname[MAX_PATH];
/* function definitions */
void AddRec(void)
{
PERSON per;
printf("Adı soyadı : ");
fflush(stdin);
gets(per.name);
printf("No : ");
scanf("%d", &per.no);
per.delflag = NORMALREC;
fseek(f, 0, SEEK_END);
fwrite(&per, sizeof(PERSON), 1, f);
}
void FindRec(void)
{
PERSON per;
char name[30];
printf("lütfen kayıt ismini giriniz :");
fflush(stdin);
gets(name);
fseek(f, 0, SEEK_SET);
while (fread(&per, sizeof(PERSON), 1, f) > 0) {
if (per.delflag != NORMALREC)
continue;
if (!stricmp(per.name, name)) {
printf("\n%s %d\n\n", per.name, per.no);
return;
}
}
printf("\nKayıt bulunamadı...\n\n");
}
void ListRec(void)
{
PERSON per;
putchar('\n');
fseek(f, 0L, SEEK_SET);
while (fread(&per, sizeof(PERSON), 1, f) > 0) {
printf("\n%20s %5d", per.name, per.no);
if (per.delflag == DELETED)
printf("\tDELETED");
}
putchar('\n\n');
}
void DelRec(void)
{
char name[30];
PERSON per;
printf("Silinecek kayıtın adı ve soyadı : ");
fflush(stdin);
gets(name);
fseek(f, 0, SEEK_SET);
while (fread(&per, sizeof(PERSON), 1, f) > 0) {
if (!stricmp(per.name, name)) {
per.delflag = DELETED;
fseek(f, -(long)sizeof(PERSON), 1);
fwrite(&per, sizeof(PERSON), 1, f);
printf("Record deleted!..\n");
return;
}
}
printf("silinecek kayıt bulunamadı");
}
void EditRec(void)
{
char name[30];
PERSON per;
printf("Değiştirilecek kayıtın adı ve soyadı : ");
fflush(stdin);
gets(name);
fseek(f, 0, SEEK_SET);
while (fread(&per, sizeof(PERSON), 1, f) > 0) {
if (per.delflag == NORMALREC && !stricmp(per.name, name)) {
printf("Adı soyadı : ");
fflush(stdin);
gets(per.name);
printf("No : ");
scanf("%d", &per.no);
fseek(f, -(long)sizeof(PERSON), 1);
fwrite(&per, sizeof(PERSON), 1, f);
printf("Record updated!..\n");
return;
}
}
printf("değiştirilecek kayıt bulunamadı\n");
}
int GetOption(void)
{
int option;
printf("\n1) Kayıt Ekle\n");
printf("2) Kayıt Listele\n");
printf("3) Kayıt Bul\n");
printf("4) Kayıt Sil\n");
printf("5) Kayıt değiştir\n");
printf("6) Pack\n");
printf("7) Sırala\n");
printf("8) Çık\n");
printf("\nSeçiminiz :");
scanf("%d", &option);
if (option <> 8)
return INVALID;
return option;
}
void SortRec(void)
{
PERSON per[2], tmp;
int i, count, chgFlag;
fseek(f, 0, SEEK_END);
count = ftell(f) / sizeof(PERSON);
do {
chgFlag = 0;
for (i = 0; i < count - 1; ++i) {
fseek(f, (long)i * sizeof(PERSON), SEEK_SET);
if (fread(per, sizeof(PERSON), 2, f) != 2) {
printf("cannot read from the file!..\n");
exit(EXIT_FAILURE);
}
if (per[0].no > per[1].no) {
chgFlag = 1;
tmp = per[0];
per[0] = per[1];
per[1] = tmp;
fseek(f, (long)i * sizeof(PERSON), SEEK_SET);
if (fwrite(per, sizeof(PERSON), 2, f) != 2) {
printf("cannot read from the file!..\n");
exit(EXIT_FAILURE);
}
}
}
} while (chgFlag);
}
void Pack(void)
{
FILE *fnew;
PERSON per;
if ((fnew = fopen("temp", "wb")) == NULL) {
printf("cannot create temporary file!..\n");
exit(EXIT_FAILURE);
}
fseek(f, 0l, SEEK_SET);
while (fread(&per, sizeof(PERSON), 1, f) > 0) {
if (per.delflag == NORMALREC)
fwrite(&per, sizeof(PERSON), 1, fnew);
}
fclose(fnew);
fclose(f);
if (unlink(fname)) {
printf("Fatal Error : Cannot open database file!..\n");
exit(EXIT_FAILURE);
}
if (rename("temp", fname)) {
printf("fatal Error: cannot delete database file!..\n");
exit(EXIT_FAILURE);
}
if ((f = fopen(fname, "r+b")) == NULL) {
printf("Fatal Error : Cannot open database file!..\n");
exit(EXIT_FAILURE);
}
printf("Pack operation succesfully completed!..\n");
}
void main()
{
char dfname[MAX_PATH];
int option;
printf("Data File : ");
gets(dfname);
if ((f = fopen(dfname, "r+b")) == NULL)
if ((f = fopen(dfname, "w+b")) == NULL) {
printf("Cannot open database file!..\n");
exit(EXIT_FAILURE);
}
strcpy(fname, dfname);
for (;;) {
option = GetOption();
switch (option) {
case ADDREC : AddRec(); break;
case LISTREC : ListRec(); break;
case FINDREC : FindRec(); break;
case DELREC : DelRec(); break;
case EDITREC : EditRec(); break;
case PACKRECS : Pack(); break;
case SORTREC : SortRec(); break;
case EXITPROG : goto EXIT;
case INVALID : printf("Geçersiz Seçenek!..\n");
}
}
EXIT:
fclose(f);
}
Uygulama 2: developer's back up programı (G. Aslan)
#include
#include
#include
#include
#include
#define GOOD 0
#define FAIL (-1)
#define FBUFSIZ (63*512) /* Buffer size */
char *Buffer, *in_buf, *out_buf;
char CmpFile[128], OutFile[128];
int filecmp(FILE *fi, FILE *fo)
{
int c1, c2;
long l1, l2;
l1 = filelength(fileno(fi));
l2 = filelength(fileno(fo));
if (l1 != l2)
return FAIL;
rewind(fi);
rewind(fo);
for (;;) {
c1 = getc(fi);
c2 = getc(fo);
if (c1 != c2) {
return FAIL;
}
if (c1 == EOF)
break;
}
return GOOD;
}
int filecopy(FILE *fi, FILE *fo)
{
int c, nbytes;
rewind(fi);
rewind(fo);
for (;;) {
c = fread(Buffer, 1, FBUFSIZ, fi);
if (c == 0) {
break;
}
nbytes = fwrite(Buffer, 1, c, fo);
if (nbytes != c) {
return FAIL;
}
}
return GOOD;
}
int main(int argc, char *argv[])
{
FILE *fi, *fo;
char *ptr;
int i;
printf("*GA Developer's Backup Utility. Version 1.0\n"
"(C) *GA, 1995\n\n");
if (argc != 2) {
fprintf(stderr, "Usage: BK \n");
return 1;
}
if ((Buffer = malloc(FBUFSIZ)) == NULL
(in_buf = malloc(FBUFSIZ)) == NULL
(out_buf = malloc(FBUFSIZ)) == NULL) {
fprintf(stderr, "Not enough memory\n");
return 2;
}
if ((fi = fopen(argv[1], "rb")) == NULL) {
ptr = argv[1];
OPN_ERR:
fprintf(stderr, "File could not be opened: '%s'\n", ptr);
return 3;
}
setvbuf(fi, in_buf, _IOFBF, FBUFSIZ);
strcpy(CmpFile, argv[1]);
ptr = strchr(CmpFile, '.');
if (ptr == NULL)
ptr = strchr(CmpFile, '\0');
for (i = 1; i <= 999; ++i) {
sprintf(ptr, ".%03d", i);
if (access(CmpFile, 0))
break;
}
if (i == 1000) {
fprintf(stderr, "Backup operation failed: File limit!\n");
return 3;
}
strcpy(OutFile, CmpFile);
if (i > 1) {
sprintf(ptr, ".%03d", i-1);
if ((fo = fopen(CmpFile, "rb")) == NULL) {
ptr = CmpFile;
goto OPN_ERR;
}
setvbuf(fo, out_buf, _IOFBF, FBUFSIZ);
if (!filecmp(fi, fo)) {
printf("No differences encountered: '%s'\n", CmpFile);
return 0;
}
fclose(fo);
}
printf("File being copied: %s ---> %s\n", argv[1], OutFile);
if ((fo = fopen(OutFile, "wb")) == NULL) {
ptr = OutFile;
goto OPN_ERR;
}
setvbuf(fo, out_buf, _IOFBF, FBUFSIZ);
if (filecopy(fi, fo)) {
fprintf(stderr, "File copy error!\n");
return 4;
}
fcloseall();
return 0;
}
Uygulama 3 : bol.c ve bir.c programları :
bir dosyanın belirkli byte büyüklüğünde n kadar sayıda dosyaya bölünmesi ve daha sonra başka bir programla bu dosyaların tekrar birleştirilmesi.
/*******bol.c *******************/
#include
#include
#include
#define MAX_LEN 80
int main(int argc, char **argv)
{
FILE *fs, *fd;
char fsname[MAX_LEN];
char fdname[MAX_LEN] = "dos0001.xxx";
int chunk;
long no_of_chars = 0L;
int no_of_files = 0;
int ch;
if (argc != 3) {
printf("bolunecek dosyanin ismini giriniz : ");
gets(fsname);
printf("kac byte'lik parcalara bolmek istiyorsunuz?");
scanf("%d", &chunk);
}
else {
strcpy(fsname, argv[1]);
chunk = atoi(argv[2]);
}
printf("%s dosyasi %d uzunlugunda dosyalara bolunecek!\n", fsname, chunk);
fs = fopen(fsname, "rb");
if (fs == NULL) {
printf("%s dosyasi acilamiyor!\n", fsname);
exit(EXIT_FAILURE);
}
fd = NULL;
while ((ch = fgetc(fs)) != EOF) {
if (fd == NULL) {
fd = fopen(fdname, "wb");
if (fd == NULL) {
printf(" %s dosyasi yaratilamiyor!\n", fdname);
exit(EXIT_FAILURE);
}
no_of_files++;
printf("%s dosyasi yaratildi!\n", fdname);
}
fputc(ch, fd);
no_of_chars++;
if (no_of_chars % chunk == 0) {
fclose(fd);
printf("%s dosyasi kapatildi!\n", fdname);
fd = NULL;
sprintf(fdname, "dos%04d.xxx", no_of_files + 1);
}
}
fclose(fs);
if (no_of_chars % chunk != 0) {
fclose(fd);
printf("%s dosyasi kapatildi!\n", fdname);
}
printf("%ld uzunlugunda %s dosyasi %d uzunlugunda %d adet dosyaya bolundu!\n",
no_of_chars, fsname, chunk, no_of_files);
return 0;
}
/*******bir.c *******************/
#include
#include
#include
#define MAX_LEN 80
int main(int argc, char **argv)
{
FILE *fs, *fd;
char fdname[MAX_LEN];
char fsname[MAX_LEN] = "dos0001.xxx";
int ch;
int no_of_files = 0;
long no_of_chars = 0L;
int k;
if (argc != 2) {
printf("birlestirilecek dosyanin ismini giriniz : ");
gets(fdname);
}
else {
strcpy(fdname, argv[1]);
}
fd = fopen(fdname, "wb");
if (fd == NULL) {
printf("%s dosyasi yaratilamiyor!\n", fdname);
exit(EXIT_FAILURE);
}
printf("%s dosyasi yaratildi!\n", fdname);
while (fs = fopen(fsname, "rb")) {
no_of_files++;
printf("%s dosyasi acildi!\n", fsname);
while ((ch = fgetc(fs)) != EOF) {
fputc(ch, fd);
no_of_chars++;
}
fclose(fs);
printf("%s dosyasi kapatildi!\n", fsname);
sprintf(fsname, "dos%04d.xxx", no_of_files + 1);
}
fclose(fd);
printf("%s dosyasi kapatildi!\n", fdname);
printf("%d adet dosya %ld uzunlugunda %s ismli dosya altinda\ birlestirildi!\n", no_of_files, no_of_chars, fdname);
for (k = 1; k <= no_of_files; ++k) {
sprintf(fsname, "dos%04d.xxx", k);
remove(fsname);
printf("%s dosyasi silindi!\n", fsname);
}
return 0;
}
8 Aralık 2007 Cumartesi
Kaydol:
Kayıt Yorumları (Atom)
Hiç yorum yok:
Yorum Gönder