Dosya İşlemleri (fstream)
Programlar sadece ekranda çalışıp kapanmaz. Verileri bir yere kaydetmen, daha sonra tekrar okuman gerekir. İşte C++'ta dosya işlemleri bunun için var. fstream kütüphanesi, dosyalarla tıpkı cin ve cout gibi çalışmanı sağlar.
Dosya işlemlerini bir kütüphane gibi düşün. Kitap okumak istiyorsan raftan alırsın (ifstream), yeni bir kitap yazmak istiyorsan boş bir deftere yazarsın (ofstream), hem okuyup hem not almak istiyorsan ikisini birden yaparsın (fstream). Ama her durumda önce kütüphaneye girmeli (dosyayı açmalı), işin bitince çıkmalısın (dosyayı kapatmalı).
ifstream ile Dosyadan Okuma
ifstream (input file stream), dosyadan veri okumak için kullanılır. Kullanımı cin'e çok benzer.
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ifstream dosya("veriler.txt");
if (!dosya.is_open()) {
std::cerr << "Dosya acilamadi!\n";
return 1;
}
std::string kelime;
while (dosya >> kelime) {
std::cout << kelime << "\n";
}
dosya.close();
return 0;
}dosya >> kelime ifadesi, tıpkı cin >> kelime gibi boşluklara göre ayrılmış kelimeleri tek tek okur. Dosyanın sonuna gelince döngü biter.
is_open() kontrolü çok önemli. Dosya yoksa veya izin problemi varsa, kontrol etmeden okuma yapmaya çalışırsan belirsiz sonuçlar alırsın.
⚠️ Dosyayı her zaman açıldıktan sonra kontrol et.
is_open()veya!dosyagibi ifadelerle dosyanın başarıyla açılıp açılmadığını doğrula. Aksi halde programın sessizce hatalı çalışır.
Sayısal Veri Okuma
Dosyada sayılar varsa, doğrudan int veya double değişkenlere okuyabilirsin:
#include <iostream>
#include <fstream>
int main() {
std::ifstream dosya("sayilar.txt");
if (!dosya) {
std::cerr << "Dosya bulunamadi!\n";
return 1;
}
int sayi;
int toplam = 0;
int adet = 0;
while (dosya >> sayi) {
toplam += sayi;
adet++;
}
std::cout << "Toplam: " << toplam << "\n";
std::cout << "Ortalama: " << (adet > 0 ? toplam / adet : 0) << "\n";
return 0;
}ofstream ile Dosyaya Yazma
ofstream (output file stream), dosyaya veri yazmak için kullanılır. cout ile aynı mantıkta çalışır.
#include <iostream>
#include <fstream>
int main() {
std::ofstream dosya("cikti.txt");
if (!dosya.is_open()) {
std::cerr << "Dosya olusturulamadi!\n";
return 1;
}
dosya << "Merhaba Dunya!\n";
dosya << "Bu bir test dosyasidir.\n";
dosya << "Sayi: " << 42 << "\n";
dosya.close();
std::cout << "Dosya basariyla yazildi.\n";
return 0;
}Varsayılan olarak ofstream dosyayı sıfırdan oluşturur. Dosya zaten varsa içeriği silinir ve baştan yazılır. Mevcut içeriğin üstüne eklemek istiyorsan ios::app modunu kullanmalısın (birazdan göreceğiz).
Yapılandırılmış Veri Yazma
CSV gibi yapılandırılmış formatlarda veri yazmak çok yaygındır:
#include <fstream>
#include <vector>
#include <string>
struct Ogrenci {
std::string ad;
int numara;
double ortalama;
};
int main() {
std::vector<Ogrenci> ogrenciler = {
{"Ali Veli", 101, 3.5},
{"Ayse Kaya", 102, 3.8},
{"Mehmet Can", 103, 2.9}
};
std::ofstream dosya("ogrenciler.csv");
dosya << "Ad,Numara,Ortalama\n"; // baslik satiri
for (const auto& ogr : ogrenciler) {
dosya << ogr.ad << "," << ogr.numara << "," << ogr.ortalama << "\n";
}
dosya.close();
return 0;
}Satır Satır Okuma — getline
>> operatörü boşluklarda durur. Peki bir satırın tamamını — boşluklar dahil — okumak istersen? std::getline() kullanırsın.
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ifstream dosya("notlar.txt");
if (!dosya) {
std::cerr << "Dosya acilamadi!\n";
return 1;
}
std::string satir;
int satirNo = 1;
while (std::getline(dosya, satir)) {
std::cout << satirNo << ": " << satir << "\n";
satirNo++;
}
dosya.close();
return 0;
}getline her çağrıda bir satır okur (newline karakterine kadar). Dosyanın sonuna gelince false döndürür ve döngü biter.
getline ile Ayrıştırma (Parsing)
CSV dosyalarını okurken getline ile satırı alıp, sonra stringstream ile parçalayabilirsin:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
int main() {
std::ifstream dosya("ogrenciler.csv");
std::string satir;
// Baslik satirini atla
std::getline(dosya, satir);
while (std::getline(dosya, satir)) {
std::stringstream ss(satir);
std::string ad, numaraStr, ortalamaStr;
std::getline(ss, ad, ',');
std::getline(ss, numaraStr, ',');
std::getline(ss, ortalamaStr, ',');
std::cout << "Ad: " << ad
<< " | Numara: " << numaraStr
<< " | Ortalama: " << ortalamaStr << "\n";
}
return 0;
}std::getline(ss, ad, ',') — üçüncü parametre ayırıcı karakterdir (delimiter). Varsayılan '\n''dir ama burada virgül kullandık.
💡 `>>` ve `getline`'ı aynı stream'de karıştırma.
>>newline karakterini stream'de bırakır, ardındangetlineçağrılırsa boş satır okur. Aradastd::wsveya boş birgetlinekullanarak tampondaki newline'ı temizle.
fstream ile Hem Okuma Hem Yazma
fstream sınıfı hem okuma hem yazma yapabilir. Ama genellikle dosya pozisyonunu manuel yönetmen gerekir, bu da işi karmaşıklaştırır.
#include <iostream>
#include <fstream>
#include <string>
int main() {
// Oncelikle dosya olustur
std::ofstream olustur("veri.txt");
olustur << "Satir 1\nSatir 2\nSatir 3\n";
olustur.close();
// Simdi hem oku hem yaz
std::fstream dosya("veri.txt", std::ios::in | std::ios::out);
if (!dosya) {
std::cerr << "Dosya acilamadi!\n";
return 1;
}
// Oku
std::string satir;
while (std::getline(dosya, satir)) {
std::cout << "Okunan: " << satir << "\n";
}
// Hata bayraklarini temizle (EOF'a ulastik)
dosya.clear();
// Dosyanin sonuna git ve yeni satir ekle
dosya.seekp(0, std::ios::end);
dosya << "Satir 4 — yeni eklendi\n";
dosya.close();
return 0;
}Dikkat edilmesi gereken noktalar:
dosya.clear()çağrısı şart. Dosya sonuna gelinceeofbayrağı set edilir, bu temizlenmeden yazma yapamazsın.seekp()yazma pozisyonunu,seekg()okuma pozisyonunu ayarlar.
Pratikte hem okuma hem yazma gerektiren durumlar için genellikle dosyayı ayrı ayrı açmak daha temiz ve güvenlidir.
Dosya Açma Modları
Dosya açarken çeşitli modlar belirleyebilirsin. Bu modlar ios namespace'i altında tanımlıdır ve | (OR) operatörü ile birleştirilebilir.
| Mod | Açıklama |
|---|---|
ios::in | Okuma modu (ifstream varsayılanı) |
ios::out | Yazma modu (ofstream varsayılanı) |
ios::app | Dosyanın sonuna ekle (append) |
ios::trunc | Dosya varsa içeriğini sil (truncate) |
ios::binary | Binary mod |
ios::ate | Dosyayı aç ve imleci sona taşı (at end) |
Dosyanın Sonuna Ekleme (Append)
Log dosyaları gibi sürekli büyüyen dosyalar için ios::app kullanılır:
#include <fstream>
#include <string>
#include <ctime>
void logYaz(const std::string& mesaj) {
std::ofstream dosya("uygulama.log", std::ios::app);
if (!dosya) return;
auto simdi = std::time(nullptr);
dosya << std::ctime(&simdi);
dosya << " " << mesaj << "\n";
dosya << "---\n";
}
int main() {
logYaz("Uygulama basladi");
logYaz("Islem tamamlandi");
logYaz("Uygulama kapandi");
return 0;
}Her çağrıda dosya açılır, sonuna ekleme yapılır ve kapanır. Mevcut içerik asla silinmez.
Dosya İçeriğini Silip Baştan Yazma (Truncate)
ios::trunc, dosya varsa içeriğini tamamen siler. ofstream varsayılan olarak ios::out | ios::trunc kullanır.
#include <fstream>
int main() {
// Bu iki satir ayni etkiyi yapar:
std::ofstream dosya1("test.txt");
std::ofstream dosya2("test.txt", std::ios::out | std::ios::trunc);
// Sadece sonuna eklemek:
std::ofstream dosya3("test.txt", std::ios::app);
return 0;
}⚠️ `ofstream` varsayılan olarak dosyayı siler! Mevcut bir dosyanın üstüne yazmak istemiyorsan
ios::appmodunu açıkça belirt. Aksi halde dosyadaki tüm veri kaybolur.
Binary Dosya Okuma ve Yazma
Şimdiye kadar metin (text) modunda çalıştık. Ama resimler, ses dosyaları veya yapılandırılmış veri gibi durumlar için binary mod gereklidir.
Text mod ile binary mod arasındaki fark: text modda '\n' karakteri platforma göre dönüştürülür (Windows'ta \r\n), binary modda veri olduğu gibi yazılır ve okunur.
Binary Yazma
#include <fstream>
#include <iostream>
struct Kayit {
char ad[50];
int yas;
double maas;
};
int main() {
Kayit k1 = {"Ali Veli", 30, 15000.50};
Kayit k2 = {"Ayse Kaya", 25, 18000.75};
std::ofstream dosya("kayitlar.bin", std::ios::binary);
if (!dosya) {
std::cerr << "Dosya olusturulamadi!\n";
return 1;
}
dosya.write(reinterpret_cast<const char*>(&k1), sizeof(Kayit));
dosya.write(reinterpret_cast<const char*>(&k2), sizeof(Kayit));
dosya.close();
std::cout << "Kayitlar yazildi. Boyut: " << 2 * sizeof(Kayit) << " byte\n";
return 0;
}write() fonksiyonu iki parametre alır: yazılacak verinin başlangıç adresi (const char* olarak) ve byte cinsinden boyutu. reinterpret_cast ile struct'ın adresini char*'a çeviririz.
Binary Okuma
#include <fstream>
#include <iostream>
struct Kayit {
char ad[50];
int yas;
double maas;
};
int main() {
std::ifstream dosya("kayitlar.bin", std::ios::binary);
if (!dosya) {
std::cerr << "Dosya acilamadi!\n";
return 1;
}
Kayit k;
while (dosya.read(reinterpret_cast<char*>(&k), sizeof(Kayit))) {
std::cout << "Ad: " << k.ad
<< " | Yas: " << k.yas
<< " | Maas: " << k.maas << "\n";
}
dosya.close();
return 0;
}read() fonksiyonu write()'ın tersidir — belirtilen byte kadar veriyi dosyadan okur ve belleğe yazar.
💡 Binary dosyalarda `std::string` veya pointer içeren struct'ları doğrudan yazamazsın. String'in iç yapısı bir pointer içerir, dosyaya pointer yazarsan okuduğunda anlamsız bir adres olur. Binary'de sabit boyutlu char dizileri (
char[50]gibi) veya serialization kütüphaneleri kullan.
Dosya Boyutunu Öğrenme
#include <fstream>
#include <iostream>
int main() {
std::ifstream dosya("kayitlar.bin", std::ios::binary | std::ios::ate);
if (!dosya) {
std::cerr << "Dosya bulunamadi!\n";
return 1;
}
auto boyut = dosya.tellg(); // imleç sonda, boyutu verir
std::cout << "Dosya boyutu: " << boyut << " byte\n";
// Basa don
dosya.seekg(0, std::ios::beg);
// ... okuma islemleri
dosya.close();
return 0;
}ios::ate dosyayı açarken imleci sona taşır. tellg() mevcut okuma pozisyonunu döndürür — bu da dosya boyutudur.
Dosya İşlemlerinde Hata Yönetimi
Dosya işlemlerinde hatalar kaçınılmazdır. Disk dolu olabilir, dosya başka bir program tarafından kilitlenmiş olabilir, izin sorunu olabilir. Her zaman kontrol et.
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>
std::string dosyaOku(const std::string& dosyaAdi) {
std::ifstream dosya(dosyaAdi);
if (!dosya) {
throw std::runtime_error("Dosya acilamadi: " + dosyaAdi);
}
std::string icerik;
std::string satir;
while (std::getline(dosya, satir)) {
icerik += satir + "\n";
}
if (dosya.bad()) {
throw std::runtime_error("Dosya okuma hatasi: " + dosyaAdi);
}
return icerik;
}
int main() {
try {
auto icerik = dosyaOku("rapor.txt");
std::cout << icerik;
}
catch (const std::exception& e) {
std::cerr << "Hata: " << e.what() << "\n";
return 1;
}
return 0;
}Stream'in durumunu kontrol eden fonksiyonlar:
good()— her şey yolundaeof()— dosya sonuna ulaşıldıfail()— işlem başarısız (format hatası gibi)bad()— ciddi hata (disk arızası gibi)
Dosya nesnesi scope'tan çıktığında destructor otomatik olarak close() çağırır. Bu RAII prensibinin bir örneğidir. Manuel close() çağrısı genellikle gereksizdir ama hata kontrolü yapacaksan kullanışlıdır.
Pratik Örnek: Basit Bir Konfigürasyon Dosyası Okuyucu
Gerçek dünyada sıkça yapılan bir iş: anahtar=deger formatında bir config dosyası okumak.
#include <fstream>
#include <iostream>
#include <map>
#include <string>
#include <sstream>
std::map<std::string, std::string> configOku(const std::string& dosyaAdi) {
std::map<std::string, std::string> config;
std::ifstream dosya(dosyaAdi);
if (!dosya) {
std::cerr << "Config dosyasi bulunamadi: " << dosyaAdi << "\n";
return config;
}
std::string satir;
while (std::getline(dosya, satir)) {
// Bos satirlari ve yorumlari atla
if (satir.empty() || satir[0] == '#') continue;
auto esittir = satir.find('=');
if (esittir != std::string::npos) {
auto anahtar = satir.substr(0, esittir);
auto deger = satir.substr(esittir + 1);
config[anahtar] = deger;
}
}
return config;
}
int main() {
auto config = configOku("ayarlar.conf");
for (const auto& [anahtar, deger] : config) {
std::cout << anahtar << " => " << deger << "\n";
}
return 0;
}Eğer ayarlar.conf dosyası şöyle olsaydı:
# Uygulama ayarlari
sunucu=localhost
port=8080
veritabani=test_dbProgram çıktısı da şöyle olurdu:
port => 8080
sunucu => localhost
veritabani => test_dbÖzet
ifstream dosyadan okuma, ofstream dosyaya yazma, fstream her ikisini birden yapar. Hepsi
<fstream>başlık dosyasında tanımlıdır.getline() ile satır satır okuma yapılır.
>>operatörü boşluklarda durur,getlinesatır sonuna kadar okur.Dosya açma modları ile davranışı kontrol edersin:
ios::app(sonuna ekle),ios::trunc(sil ve baştan yaz),ios::binary(binary mod).Binary dosyalar için
write()veread()fonksiyonları kullanılır. Sabit boyutlu yapılar binary'de güvenlidir; pointer içeren yapılar değildir.Her dosya açma işleminden sonra `is_open()` veya `!dosya` ile kontrol et. Dosya açılamazsa sessiz hata alırsın.
Dosya nesneleri scope'tan çıkınca otomatik kapanır (RAII). Yine de kritik yazma işlemlerinden sonra
close()çağırıp hata kontrolü yapabilirsin.
AI Asistan
Sorularını yanıtlamaya hazır