Anasayfa / C++ / C++ Dosya İşlemleri (File Operations)

C++ Dosya İşlemleri (File Operations)

Birçok gerçek hayat problemi çok büyük hacimli verileri işler ve bu gibi durumlarda, verileri kalıcı olarak saklamak için sabit disk (hard disk) veya disket (floppy disk) gibi aygıtlara ihtiyaç duyarız. Veriler, bu tür depolama aygıtlarında dosyalar (files) adı verilen kavram kullanılarak belirli alanlarda saklanır. C++ programları, bu dosyalar üzerinde okuma (read) ve yazma (write) işlemlerini gerçekleştirecek şekilde tasarlanabilir.

Bir bilgisayar programı tipik olarak konsol birimi ile program arasında veya program ile bir disk dosyası arasında veri iletişimi içerir. Bu kapsamlı rehberde, C++ programlama dilinde dosyalarla nasıl iletişim kuracağınızı, veri akışlarının (streams) ne olduğunu ve dosya okuma/yazma işlemlerini örneklerle sıfırdan öğreneceğiz.

1. Dosya Akışları (File Streams) Nedir?

C++’ın Girdi/Çıktı (I/O) sistemi, konsol tabanlı işlemlere çok benzeyen bir mantıkla dosya işlemlerini yönetir. Programlar ile dosyalar arasında bir arayüz olarak dosya akışlarını (file streams) kullanır.

Sistemin çalışma mantığı son derece basittir:

  • Programa veri sağlayan akışa giriş akışı (input stream) denir. Giriş akışı dosyadan verileri çıkarır (okur).
  • Programdan veri alan akışa ise çıkış akışı (output stream) denir. Çıkış akışı verileri dosyaya ekler (yazar).

2. Dosya İşlemleri İçin Kullanılan Sınıflar ()

C++ I/O sistemi, dosya işleme yöntemlerini tanımlayan bir dizi sınıfa (class) sahiptir. Disk dosyalarını yönetmek üzere tasarlanmış bu temel sınıflar ifstream, ofstream ve fstream olarak adlandırılır.

Bu sınıflar, fstreambase temel sınıfından ve iostream sınıflarından türetilmiştir. Dosya işlemi yapacağımız her C++ programının en başına mutlaka fstream başlık dosyasını dahil etmemiz (include) zorunludur.

  • ifstream: Yalnızca giriş (okuma) işlemleri sağlar.
  • ofstream: Yalnızca çıkış (yazma) işlemleri sağlar.
  • fstream: Eşzamanlı giriş ve çıkış işlemlerini destekler.

3. Bir Dosyayı Açma ve Kapatma (Opening and Closing Files)

Eğer bir disk dosyası kullanmak istiyorsak, dosya için bir isim ve bir açılış yöntemi belirlememiz gerekir. Bir dosyayı açmak için önce uygun bir dosya akışı nesnesi (file stream object) oluşturmalı ve ardından onu istenen dosya adıyla ilişkilendirmeliyiz (link).

C++’ta bir dosya iki şekilde açılabilir:

  1. Sınıfın kurucu fonksiyonunu (constructor) kullanarak.
  2. Sınıfın open() üye fonksiyonunu kullanarak.

A. Constructor (Kurucu) İle Dosya Açmak

Bu yöntem, akışta yalnızca tek bir dosya kullanacağımız zaman tercih edilir. Örneğin, programımızdan diskteki bir dosyaya metin yazmak (çıkış yapmak) istediğimizi varsayalım. Sınıf olarak ofstream kullanmalıyız.

ofstream outfile("results.txt"); // Dosya yalnızca yazma (output) için açıldı

Yukarıdaki C++ kodu, çıkış akışını yönetecek olan outfile adında bir ofstream nesnesi oluşturur. Aynı zamanda “results.txt” adlı dosyayı açarak onu bu çıkış akışına (output stream) bağlar.

Verileri okumak (giriş) istediğimizde ise ifstream sınıfını kullanırız:

ifstream infile("data.txt"); // Dosya yalnızca okuma (input) için açıldı

Bu ifade infile adında bir nesne tanımlar ve “data.txt” dosyasını okuma işlemi için ona bağlar.

B. open() Fonksiyonu İle Dosya Açmak

Eğer aynı akış (stream) nesnesini kullanarak birden fazla dosyayı sırayla açıp işlememiz gerekiyorsa open() fonksiyonu kullanılır.

ofstream outfile;          // Önce nesne yaratılır
outfile.open("DATA1.txt"); // Nesne ilk dosyaya bağlanır
outfile.close();           // Dosya kapatılır
outfile.open("DATA2.txt"); // Aynı nesne bu kez ikinci dosyaya bağlanır
outfile.close();           // Dosya kapatılır

Bu örnekteki close() fonksiyonu, dosya ile akış nesnesi arasındaki bağlantıyı koparmak (disconnect) için kullanılır,. Dosya ile işimiz bittiğinde, işletim sistemi kaynaklarını serbest bırakmak için onu kapatmak (close) altın bir yazılım kuralıdır.

4. Pratik C++ Kod Örneği: Dosyaya Yazma ve Dosyadan Okuma

Aşağıdaki program, klavyeden kullanıcıdan bir metin ve sayı alır, bunu bir dosyaya kaydeder, dosyayı kapatır. Ardından aynı dosyayı tekrar okuma modunda açarak içindeki verileri ekrana basar.

#include <iostream>
#include <fstream> // Dosya işlemleri için şart!
#include <string>

using namespace std;

int main() {
    string esyaAdi;
    float fiyat;

    // 1. ADIM: DOSYAYA YAZMA İŞLEMİ (OUTPUT)
    ofstream outf("ENVANTER.txt"); // Çıkış akışı nesnesi oluştur ve dosyayı aç

    cout << "Esya adini girin: ";
    cin >> esyaAdi;
    outf << esyaAdi << "\n";       // Veriyi dosyaya yazdır

    cout << "Esya fiyatini girin: ";
    cin >> fiyat;
    outf << fiyat << "\n";         // Veriyi dosyaya yazdır

    outf.close(); // İşlem bitti, dosyayı güvenle kapat.

    // 2. ADIM: DOSYADAN OKUMA İŞLEMİ (INPUT)
    ifstream inf("ENVANTER.txt"); // Aynı dosyayı bu sefer okuma akışı ile aç

    inf >> esyaAdi;               // Dosyanın ilk satırını oku ve değişkene ata
    inf >> fiyat;                 // Dosyanın ikinci satırını oku ve değişkene ata

    cout << "\n--- DOSYADAN OKUNAN BILGILER ---\n";
    cout << "Esya Adi: " << esyaAdi << "\n";
    cout << "Fiyati  : " << fiyat << "\n";

    inf.close(); // Dosyayı tekrar kapat.

    return 0;
}

5. Dosya Modları ve Güvenlik (File Modes)

Dosyaları open() fonksiyonu veya kurucular aracılığıyla açarken sadece dosya adını verdik. Aslında bu fonksiyonlar, dosyanın hangi amaçla açılacağını belirten ikinci bir parametre daha alırlar; buna “dosya modu parametresi” (file mode parameter) denir.

Eğer ikinci argümanı yazmazsak, C++ bazı varsayılan (default) değerler kullanır.

  • ifstream için varsayılan mod ios::in (sadece okuma için) şeklindedir.
  • ofstream için varsayılan mod ios::out (sadece yazma için) şeklindedir.

Çok Önemli Güvenlik Uyarısı: Bir dosya yalnızca yazma işlemi (ios::out) için açıldığında, o isimde bir dosya sistemde zaten mevcutsa, orijinal içeriği tamamen silinir ve karşınıza temiz/boş bir dosya çıkarılır.

Eğer var olan bir dosyanın içeriğini silmeden, sadece sonuna yeni bilgiler eklemek istiyorsanız dosyayı ekleme (append) modunda açmalısınız. Bu işlem ios::app modu ile yapılır.

Diğer bazı dosya modları şunlardır:

  • ios::ate: Dosya açılırken doğrudan dosya sonuna (end-of-file) gider.
  • ios::binary: Dosyayı metin olarak değil, ikili (binary) formatta açar.
  • ios::nocreate: Dosya mevcut değilse açma işlemi başarısız olur.
  • ios::trunc: Zaten var olan bir dosyanın içeriğini siler.

Örneğin, var olan bir dosyaya veri eklemek ve dosya yoksa hata almak için OR operatörü (|) kullanarak iki modu birleştirebiliriz:

fout.open("data.txt", ios::app | ios::nocreate);

Bu kod, dosyayı ekleme (append) modunda açar, ancak dosya yoksa açma işlemi başarısız olur. (Not: fstream sınıfını kullanırken varsayılan bir mod atanmaz; modu bizim ios::in | ios::out şeklinde açıkça belirtmemiz gerekir.)

6. Dosya Sonunu Tespit Etmek (Detecting end-of-file)

Bir dosyadan bir while döngüsü aracılığıyla satır satır veya karakter karakter veri okurken, dosyanın sonuna gelip gelmediğimizi bilmek zorundayız. Aksi halde program olmayan verileri okumaya çalışır ve hata verir. Dosyadan daha fazla veri okunmasını engellemek için dosya sonu koşulunun tespit edilmesi gereklidir.

Bu işlem için ios sınıfının bir üye fonksiyonu olan eof() (End of File) kullanılır. Dosya okuma sırasında dosya sonuna ulaşılırsa bu fonksiyon sıfırdan farklı bir değer (true) döndürür; aksi halde sıfır (false) döndürür.

ifstream dosya("liste.txt");
string kelime;

// eof() sıfır olduğu sürece (dosya sonuna gelmedikçe) döngü çalışır
while(dosya.eof() == 0) {
    dosya >> kelime;
    cout << kelime << " ";
}

7. İleri Düzey: Dosya İşaretçileri ve Konumlandırma (File Pointers)

Büyük metin veritabanlarıyla çalışırken dosyayı her zaman baştan sona okumak istemeyebilirsiniz. C++’ta her dosyanın bir “giriş işaretçisi” (get pointer) ve bir “çıkış işaretçisi” (put pointer) bulunur.

  • Okuma (read) işlemi için giriş işaretçisi kullanılır.
  • Yazma (write) işlemi için çıkış işaretçisi kullanılır. Her işlem yapıldığında uygun işaretçi otomatik olarak ilerletilir.

Bu işaretçileri dosya içerisinde istediğimiz herhangi bir bayta (konuma) taşımak mümkündür. C++ dosya akış sınıfları bu durumları yönetmek için şu fonksiyonları destekler:

  • seekg(): Giriş işaretçisini (get pointer) belirtilen bir konuma taşır.
  • seekp(): Çıkış işaretçisini (put pointer) belirtilen bir konuma taşır.
  • tellg(): Giriş işaretçisinin mevcut konumunu bayt (byte) cinsinden verir.
  • tellp(): Çıkış işaretçisinin mevcut konumunu bayt cinsinden verir.

Örneğin; infile.seekg(10); ifadesi, dosya işaretçisini baştan itibaren 10. bayta taşır. (Dosyalardaki baytların sıfırdan başlayarak numaralandırıldığını unutmayın; dolayısıyla bu, dosyadaki 11. bayta işaret edecektir.) Ayrıca işaretçi hareketlerini dosyanın başından (ios::beg), bulunduğumuz noktadan (ios::cur) veya dosyanın sonundan (ios::end) geriye doğru olacak şekilde de ayarlayabiliriz.

Özet

C++ programlamada nesneleri ve verileri geçici RAM bellek yerine kalıcı bir depolama ünitesine kaydetmek, kurumsal projelerin temelidir. ifstream, ofstream sınıfları ve <fstream> başlık dosyası sayesinde programınıza entegre edeceğiniz bu işlemler; oyun kayıt dosyaları oluşturmaktan log (kayıt) mekanizmalarına kadar devasa çözümler yaratmanızı sağlar. Verilerinizi kaybetmemek adına her işlemden sonra dosyayı güvenle kapatmayı ve veri yazma modlarını dikkatlice kullanmayı unutmayın. Mutlu kodlamalar!

Etiketlendi:

Cevap bırakın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir