====== Dosya Girdi ve Çıktı ======
Bu başlıktaki bilgiler C++17'den önce kullanılan dosya girdi/çıktı işlemleri ile ilgilidir. C++17 ile birlikte gelen filesystem kütüphanesi ile ilgili bilgiler için [[filesystem]] sayfasına bakınız.
C++'da dosya girdi ve çıktı işlemleri normal girdi çıktı işlemleri ile neredeyse aynıdır. 3 adet dosya G/Ç sınıfı bulunmaktadır.
* istream sınıfından türetilen **ifstream** sınıfı
* ostream sınıfından türetilen **ofstream** sınıfı
* iostream sınıfından türetilen **fstream** sınıfı
Bu sınıfları kullanabilmek için; **fstream** başlık dosyasını dahil etmeniz gerekmektedir.
clog, cerr, cin ve cout gibi kullanıma hazır akışlardan farklı olarak dosya akışları bizim tarafımızdan ayarlanması gerekmektedir.
* Bir dosyayı okuma ve/veya yazma amacıyla açmak için, parametre olarak dosyanın adını kullanarak uygun dosya G/Ç sınıfından bir nesne oluşturma.
* Ardından dosyaya veri yazmak veya dosyadan veri okumak için ekleme (<<) veya çıkarma (>>) operatörünü kullanma.
* İşlemleriniz bittiğinde, bir dosyayı kapatma.
Şeklinde özetlenebilir.
===== Dosya Çıktısı =====
Aşağıdaki örnekte dosya çıktısı almak için ofstream sınıfını kullanacağız.
#include
#include
int main()
{
// ofstream dosya yazmak için kullanılır
// Deneme.txt adında bir dosya oluşturacağız
std::ofstream outf{ "Deneme.txt" };
// Eğer çıktı dosya akışını yazmak için açamazsak
if (!outf)
{
// Bir hata yazdırın ve çıkın
std::cerr << "Siktir, Deneme.txt yazmak için açılamadı!\n";
return 1;
}
// Bu dosyaya iki satır yazacağız
outf << "Satir 1\n";
outf << "Satir 2\n";
return 0;
// outf kapsam dışına çıktığında, ofstream
// yıkıcı dosyayı kapatacaktır
}
===== Dosya Girdisi =====
Bir önceki başlıkta yazdığımız dosyayı okumak için ifstream sınıfını kullanacağız.
#include
#include
#include
int main()
{
// ifstream dosyaları okumak için kullanılır
// Deneme.txt adlı bir dosyadan okuyacağız
std::ifstream inf{ "Deneme.txt" };
// Eğer çıktı dosya akışını okumak için açamazsak
if (!inf)
{
// Bir hata yazdırın ve çıkın
std::cerr << "Siktir, Deneme.txt okunmak için açılamadı!\n";
return 1;
}
// Hala okunacak şeyler varken
while (inf)
{
// dosyadan bir dizeye bir şeyler oku ve yazdır
std::string strInput;
inf >> strInput;
std::cout << strInput << '\n';
}
return 0;
// inf kapsam dışına çıktığında, ifstream
// yıkıcı dosyayı kapatacaktır
}
Yukarıdaki örnek tam istediğimiz sonucu elde etmemizi sağlamayabilir. Çünkü çıkarma operatörü bildiğimiz gibi boşluk karakterlerinde kesilir.
Bu yüzden ''getline()'' fonksiyonu kullanabiliriz.
#include
#include
#include
int main()
{
// ifstream dosyaları okumak için kullanılır
// Deneme.txt adlı bir dosyadan okuyacağız
std::ifstream inf{ "Deneme.txt" };
// Eğer çıktı dosya akışını okumak için açamazsak
if (!inf)
{
// Bir hata yazdırın ve çıkın
std::cerr << "Siktir, Deneme.txt okunmak için açılamadı!\n";
return 1;
}
// Hala okunacak şeyler varken
while (inf)
{
// dosyadan bir dizeye bir şeyler oku ve yazdır
std::string strInput;
std::getline(inf, strInput);
std::cout << strInput << '\n';
}
return 0;
// inf kapsam dışına çıktığında, ifstream
// yıkıcı dosyayı kapatacaktır
}
===== Tampon Çıktı =====
C++'da çıktı tamponlanabilir.
Bu, bir dosya akışına çıktı olarak verilen herhangi bir şeyin hemen diske yazılmayabileceği anlamına gelir.
Bunun yerine, birkaç çıktı işlemi birleştirilebilir ve birlikte işlenebilir.
Bu öncelikle performans nedenleriyle yapılır. Bir tampon diske yazıldığında, buna tamponun boşaltması denir. Tamponun boşaltılmasını sağlamanın bir yolu dosyayı kapatmaktır - tampunun içeriği diske yazılacak ve ardından dosya kapatılacaktır.
Tamponlama genellikle bir sorun değildir, ancak bazı durumlarda dikkatsiz kişiler için komplikasyonlara neden olabilir.
Bu durumda ana suçlu, tamponda veri olması ve ardından programın hemen sonlanmasıdır (ya çökerek ya da ''exit()'' fonksiyonunu çağırarak).
Bu durumlarda, dosya akışı sınıflarının yıkıcıları çalıştırılmaz, bu da dosyaların asla kapatılmadığı ve tamponların asla boşaltılmadığı anlamına gelir.
Bu durumda, tampondaki veriler diske yazılmaz ve sonsuza kadar kaybolur. Bu nedenle ''exit()'' fonksiyonunu çağırmadan önce açık dosyaları kapatmak her zaman iyi bir fikirdir.
Tamponu ''ostream::flush()'' fonksiyonunu kullanarak ya da ''std::flush'''ı çıktı akışına göndererek manuel olarak boşaltmak mümkündür. Bu yöntemlerden her ikisi de programın çökmesi ihtimaline karşı tamponun içeriğinin hemen diske yazılmasını sağlar.
''std::endl'' çıktı akışını boşaltır. ''std::endl'''in aşırı kullanımı (gereksiz tampon boşaltmalarına neden olur), boşaltmaların pahalı olduğu (bir dosyaya yazma gibi) tamponlu G/Ç yaparken performans etkilerine neden olabilir.
Bu nedenle, performans bilincine sahip programcılar, tamponun gereksiz yere yıkanmasını önlemek amacıyla, çıktı akışına yeni bir satır eklemek için ''std::endl'' yerine genellikle ''%%\n%%'' kullanırlar.
===== Dosya Modları =====
Dosya akışı sınıflarının yapıcıları, dosyanın nasıl açılması gerektiğine ilişkin bilgileri belirtmemize olanak sağlayan opsiyonel **dosya modu** adı verilen ikinci bir parametreye sahiptir.
Bu parametrenin kabul ettiği bayraklar ''ios'' sınıfında bulunur.
^ ios dosya modu ^ Açıklama ^
| ''app'' | Dosyayı ekleme modunda açar |
| ''ate'' | Okumadan/yazmadan önce dosyanın sonuna kadar arar |
| ''binary'' | Dosyayı ikili modda açar |
| ''in'' | Dosyayı okuma modunda açar (ifstream için varsayılan) |
| ''out'' | Dosyayı yazma modunda açar (ofstream için varsayılan) |
| ''trunc'' | Dosya zaten mevcutsa siler |
Birden fazla bayrağı, ''|'' operatörünü kullanarak belirtmek mümkündür.
''fstream'''in tasarlanma şekli nedeniyle, ''std::ios::in'' kullanılırsa ve açılan dosya mevcut değilse başarısız olabilir. Eğer ''fstream'' kullanarak yeni bir dosya oluşturmanız gerekiyorsa, sadece ''std::ios::out'' modunu kullanın.
===== Rastgele Dosya G/Ç =====
Tüm dosya akışları, bir dosya işaretçisine sahiptir. Bu işaretçi, geçerli dosya okuma/yazma konumunu takip eder.
Genel olarak bir dosya okuma/yazma için açıldığında bu işaretçi dosyanın başında bulunur. Eğer ''app'' dosya modu ile açtıysak, dosyanın sonuna taşınır ki ekleyeceğimiz veriler dosyanın sonuna eklensin.
Yazının bu kısmına kadarki kısımda yaptığımız g/ç işlemleri hep sıralı şekildeydi. İstersek rasgele şekilde dosyanın istediğimiz kısımlarına erişebiliriz. Bunun için işaretçiyi istediğimiz yere taşımamız gerekmektedir.
Bunu dosya işaretçisini ''seekg()'' ve ''seekp()'' fonksiyonları ile manipüle ederek gerçekleştiririz.
Normal akışlarda ''seekg()'' okuma pozisyonunu, ''seekp()'' yazma pozisyonunu ayrı olarak manipüle ederler. Ancak dosya g/ç söz konusu olduğunda okuma ve yazma pozisyonları ortaktır. Yani iki fonksiyonda birbirleri yerine kullanılabilir.
Bu iki fonksiyon iki parametreye sahiptir;
* Birinci parametre, dosya işaretçisinin kaç bayt taşınacağını belirleyen bir ofsettir.
* İkinci parametre, ofset parametresinin neye göre ofsetlenmesi gerektiğini belirten bir ios bayrağıdır.
^ ios bayrağı ^ Açıklama ^
| ''ios::beg'' | Dosyanın başından itibaren ofsetlenir |
| ''ios::cur'' | Dosyanın mevcut konumundan itibaren ofsetlenir |
| ''ios::end'' | Dosyanın sonundan itibaren ofsetlenir |
Pozitif ofset, dosya işaretçisini dosyanın sonuna doğru taşımak anlamına gelirken, negatif ofset dosya işaretçisini dosyanın başına doğru taşımak anlamına gelir.
#include
#include
#include
int main()
{
std::ifstream inf{ "Deneme.txt" };
// Giriş dosyası akışını okumak için açamadıysak
if (!inf)
{
// Bir hata yazdırın ve çıkın
std::cerr << "Siktir, Deneme.txt dosyası okunmak için açılamadı!\n";
return 1;
}
std::string strData;
inf.seekg(5); // 5. karaktere geç
// Satırın geri kalanını alın ve 2. satıra geçerek yazdırın
std::getline(inf, strData);
std::cout << strData << '\n';
inf.seekg(8, std::ios::cur); // dosyaya 8 bayt daha taşı
// Satırın geri kalanını alın ve yazdırın
std::getline(inf, strData);
std::cout << strData << '\n';
inf.seekg(-14, std::ios::end); // dosya sonundan 14 bayt önce taşı
// Satırın geri kalanını alın ve yazdırın
std::getline(inf, strData);
std::cout << strData << '\n';
return 0;
}
Bir diğer onemli fonksiyonlar ise ''tellg()'' ve ''tellp()'' fonksiyonlarıdır. Bu fonksiyonlar dosya işaretçisinin mutlak konumunu döndürürler.
Bu fonksiyonlar ile dosyanın boyutunu hesaplayabiliriz.
std::ifstream inf {"Deneme.txt"};
inf.seekg(0, std::ios::end); // dosya sonuna taşı
std::cout << inf.tellg();
Programlamada ''%%\n%%'' aslında soyut bir kavramdır. Bu karakteri biz satır sonu olarak yorumlarız.
* Windows'ta satır sonu, sıralı şekilde **CR** (//satır başı//) ve **LF** (//satır besleme//) karakterleri ile gösterilir. (Yani 2 bayt depolama alanı kaplar.)
* Unix'te satırsonu, sadece **LF** (//satır besleme//) karakteri ile gösterilir. (Yani 1 bayt depolama alanı kaplar)
Bu yüzden yukarıdaki örnek kod iki işletim sisteminde farklı sonuç verecektir.
===== fstream ile aynı anda okuma ve yazma =====
Bildiğimiz gibi //fstream// sınıfı yazma ve okuma işlemlerini birlikte destekler. Buradaki en büyük kusur, okuma ve yazma arasında keyfi olarak geçiş yapmanın mümkün olmamasıdır.
Okuma veya yazma işlemlerinden harhangi biri gerçekleşirken, diğerine geçmek istersek zorunlu olarak dosya işaretçisini modifiye etmemiz gerekir.
Eğer dosya işaretçisinin geçerli konumunu değiştirmek istemiyorsak, yani sadece diğer işleme geçmek istiyorsak, ''seek'' ile zaten bulunduğu konuma tekrar taşımamız gerekmektedir.
// iofile'ın fstream türünde bir nesne olduğunu varsayalım
iofile.seekg(iofile.tellg(), std::ios::beg); // geçerli dosya konumuna ara
Eğer bu işlemi yapmazsanız {{https://media.tenor.com/klCteN_ER1oAAAAC/shylily-gman.gif?0x150}}
Değişkenleri bir dosyaya aktarmak oldukça kolay olsa da, işaretçilerle söz konusu olduğunda işler daha karmaşık hale gelir.
Bildiğimiz gibi işaretçiler, bellek adresleri tutarlar ve programın her çalışmasında bu adresler değişebilir.
Eğer biz işaretçileri diske yazdırırsak bu o çalışma esnasında geçerli olan adresi yazdıracaktır.
Bellek adreslerini dosyalara yazmayın. Başlangıçta bu adreslerde bulunan değişkenler, değerlerini diskten geri okuduğunuzda farklı adreslerde olabilir ve adresler geçersiz olur.
----
[[tr:cs:cpp:common:fileio|UCH Viki]]'den alınmıştır.
https://wiki.ulascemh.com/doku.php?id=tr:cs:cpp:common:fileio