Giriş
Dosyanın sonuna gelindiği kontrolü (EOF) hata yapmaya açık bir konu. İnternette "EOF kontrolü yapan kodum neden çalışmıyor" şeklinde bir çok soru görülebilir.
EOF Karakteri Nedir
EOF karakteri diye bir karakter aslında yok. İşletim sistemi bir şekilde dosyanın boyunu biliyor ve sonuna geldiğimizde bize EOF - genelde -1 olarak tanımlıdır - dönüyor. Burada küçük bir açıklama yapmak lazım. Windows C Runtime ASCII 26 karakterini EOF olarak yorumluyor. Bu sadece Windows'a mahsus. Linux'ta böyle bir kural yok. Dolayısıyla Windows'ta binary bir dosyayı text olarak açarsak ve içinde tesadüfen ASCII 26 karakteri varsa henüz dosyanın sonuna gelmeden EOF alabiliriz. Aşağıdaki örnekte binary tar.gz dosyasını text modunda açınca bu durum görülebilir.
feof nasıl çalışır?
Benim için akımların (stream) nasıl bu kontrolü yaptığını anlatan bu soruyu görmek ufuk açıcı bir deneyim oldu.
C dilindeki bu metod sadece ve sadece bir akımın eof bayrağının atanıp atanmadığını kontrol eder. Yani bu metod okuma işlemi yapmaz!
Bu bayrağın kaldırılması için, akımın en son karakterini de okuduktan sonra, bir tane daha okuma işlemi yapmamız gerekir! Aynı kural C++ için de geçerlidir.
Konuyu açıklayan küçük bir örnek
Döngü Koşulu Olarak feof Kullanmak
Yukarıdaki açıklamayı okuduktan sonra döngü koşulu olarak feof kullanmak istiyorsa, bir tane read işleminin hata döndüreceğini kabul etmesi gerektiğini kabul ederek işe başlamak gerekir. Dolayısıyla her read işleminin sonucunu kontrol etmemiz lazım gelir. Yani mantra şu
Yanlış Kullanım Örneği - Yapmayın!
Örnek'te dosyanın sonuna gelinceye kadar sayılar okunuyor. Son sayılar da okunduktan sonra feof bir kere daha çağırılıyor. Ancak henüz akımın sonunu geçmedik. Yani Bir read işlemi akımın eof bayrağını atamadı. Dolayısıyla feof true döndürür ve fscanf bir kere daha çalışır. Bu sefer fscanf akımın eof bayrağını atar. Ancak fscanf için döndürdüğü sonuç kontrolü yapılmadığı için printf çalıştırılır. Ve son satır iki kere gösterilir!
Bu örnek mantrayı ihlal eder.
Doğru Kullanım Örneği
İllaki feof ile çalışmak istiyorsak read işleminden sonra okumanın başarılı olduğunu kontrol etmek gerekir.
Döngü Koşulu Olarak Read İşlemi Kullanmak
Bu örneklerde dosyada hatalı satır olması ile ilgilenmeyiz. Sadece tüm satırları okumaya çalışırız. Aşağıdaki örneklerin hepsi doğru çalışır.
fgetc kullanımı
while döngüsünden çıktından sonra ferror kontrolü bence isteğe bağlı. EOF'tan dolayı mı yoksa okuma hatasından dolayı mı çıktığımızı anlamaya yarar.
while döngüsünden çıktından sonra ferror kontrolü bence isteğe bağlı. EOF'tan dolayı mı yoksa okuma hatasından dolayı mı çıktığımızı anlamaya yarar.
C++ ile getline kullanarak bir satırı okumak mümkün. Döngüden EOF yüzünden mi yoksa satır sonu geldiği için mi çıkıldığını anlamak kolay.
Örnek
fread yazısına taşıdım.
Dosyanın sonuna gelindiği kontrolü (EOF) hata yapmaya açık bir konu. İnternette "EOF kontrolü yapan kodum neden çalışmıyor" şeklinde bir çok soru görülebilir.
EOF Karakteri Nedir
EOF karakteri diye bir karakter aslında yok. İşletim sistemi bir şekilde dosyanın boyunu biliyor ve sonuna geldiğimizde bize EOF - genelde -1 olarak tanımlıdır - dönüyor. Burada küçük bir açıklama yapmak lazım. Windows C Runtime ASCII 26 karakterini EOF olarak yorumluyor. Bu sadece Windows'a mahsus. Linux'ta böyle bir kural yok. Dolayısıyla Windows'ta binary bir dosyayı text olarak açarsak ve içinde tesadüfen ASCII 26 karakteri varsa henüz dosyanın sonuna gelmeden EOF alabiliriz. Aşağıdaki örnekte binary tar.gz dosyasını text modunda açınca bu durum görülebilir.
int oneChar;
iFile = fopen("myFile.tar.gz", "r");
while ((oneChar = fgetc(iFile)) != EOF) {
printf("%d ", oneChar);
}
Aslında dosya şöyle açılmalıydı.fopen("myFile.tar.gz", "rb");
feof nasıl çalışır?
Benim için akımların (stream) nasıl bu kontrolü yaptığını anlatan bu soruyu görmek ufuk açıcı bir deneyim oldu.
Bu bayrağın kaldırılması için, akımın en son karakterini de okuduktan sonra, bir tane daha okuma işlemi yapmamız gerekir! Aynı kural C++ için de geçerlidir.
Konuyu açıklayan küçük bir örnek
std::istringstream stream("a");
char ch;
if (stream >> ch) {
std::cout << "At eof? " << std::boolalpha << stream.eof() << "\n";
std::cout << "good? " << std::boolalpha << stream.good() << "\n";
}
Bu kod şunu dönerfalse //Aslında dosya sonuna geldik ama henüz geçmedik. EOF false
true //
Aslında dosya sonuna geldik ama henüz geçmedik. Good true
Döngü Koşulu Olarak feof Kullanmak
Yukarıdaki açıklamayı okuduktan sonra döngü koşulu olarak feof kullanmak istiyorsa, bir tane read işleminin hata döndüreceğini kabul etmesi gerektiğini kabul ederek işe başlamak gerekir. Dolayısıyla her read işleminin sonucunu kontrol etmemiz lazım gelir. Yani mantra şu
Don't usegood()
oreof()
to detect eof before you try to read any further
Peki bu zahmete neden gireriz? Aklıma gelen tek örnek dosyada istenilen formata uymayan geçersiz bir satır varsa, geçersiz satır ile EOF arasındaki farkı ayırt etmek için kullanımı.
Yanlış Kullanım Örneği - Yapmayın!
Örnek'te dosyanın sonuna gelinceye kadar sayılar okunuyor. Son sayılar da okunduktan sonra feof bir kere daha çağırılıyor. Ancak henüz akımın sonunu geçmedik. Yani Bir read işlemi akımın eof bayrağını atamadı. Dolayısıyla feof true döndürür ve fscanf bir kere daha çalışır. Bu sefer fscanf akımın eof bayrağını atar. Ancak fscanf için döndürdüğü sonuç kontrolü yapılmadığı için printf çalıştırılır. Ve son satır iki kere gösterilir!
#include<stdio.h>
int main()
{
int i;
FILE * f;
f=fopen("inputA.txt","r");
while (!feof(f)){
fscanf(f, "%i", &i);
printf("%i\t", i);
}
printf("\n");
}
C++ için Yanlış Kullanım Örneği - Yapmayın!Bu örnek mantrayı ihlal eder.
ifstream foo("foo.txt");
while (foo.good()){
string bar;
getline(foo, bar);
cout << bar << endl;
}
Bu örnek te mantrayı ihlal eder.while(!inStream.eof()){
int data;
inStream >> data;
}
Doğru Kullanım Örneği
İllaki feof ile çalışmak istiyorsak read işleminden sonra okumanın başarılı olduğunu kontrol etmek gerekir.
while (!feof(fp)) {
++lineno;
while (nfields == (nret = fscanf(fp, "%f,", &vec[0]))) {
// do something with the vector read
++lineno;
}
if (ferror(fp)) {
// handle the error, usually exit/return
} else if (nret != EOF) {
fprintf(stderr, "warning: ignoring malformed line %zu\n", lineno);
fscanf(fp, "%*[^\n]");
}
}
C++'ta şöyle yapılırwhile( !in.eof() )
{
int data;
in >> data;
if ( in.fail() ) /* handle with break or throw */;
// now use data
}
Döngü Koşulu Olarak Read İşlemi Kullanmak
Bu örneklerde dosyada hatalı satır olması ile ilgilenmeyiz. Sadece tüm satırları okumaya çalışırız. Aşağıdaki örneklerin hepsi doğru çalışır.
fgetc kullanımı
while döngüsünden çıktından sonra ferror kontrolü bence isteğe bağlı. EOF'tan dolayı mı yoksa okuma hatasından dolayı mı çıktığımızı anlamaya yarar.
/*
* ch MUST be an int. Otherwise you can't distinguish between
* (unsigned char)255 and EOF, and this won't work.
*/
int ch;
while ((ch = fgetc(fp)) != EOF) {
// do something with ch
}
if (ferror(fp)) {
// handle the error, usually exit or return
} else {
// continue execution
}
fgets kullanımıwhile döngüsünden çıktından sonra ferror kontrolü bence isteğe bağlı. EOF'tan dolayı mı yoksa okuma hatasından dolayı mı çıktığımızı anlamaya yarar.
while (fgets(buffer, buffer_size, fp)) {
// do something with buffer
}
if (ferror(fp)) {
// handle the error, usually exit or return
} else {
// continue execution
}
C++ ile getlineC++ ile getline kullanarak bir satırı okumak mümkün. Döngüden EOF yüzünden mi yoksa satır sonu geldiği için mi çıkıldığını anlamak kolay.
while(getline(file,line) && !file.fail()) {}
C++ ile streamÖrnek
int data;
while(inStream >> data){
}
fread kullanımıfread yazısına taşıdım.
Hiç yorum yok:
Yorum Gönder