Neden önemli?
Program kapanınca değişkenler silinir. Anadolu Market'in sipariş listesi, Ilgaz Bank'ın günlük işlem dökümü ise kalıcı olmalı: diske yazılır, yarın yeniden okunur, başka programlara verilir. İş hayatında verinin büyük bölümü hâlâ düz metin ve CSV dosyalarında dolaşır. Bu derste önce bellekte küçük dosyalar oluşturup okuyacağız; aynı kod bilgisayarındaki gerçek dosyalarda da çalışır.
open ve with: aç, kullan, kesin kapat
open(yol, mod, encoding="utf-8") bir dosya nesnesi döndürür. with bloğu bittiğinde dosya, hata olsa bile otomatik kapanır. Kapanmayan dosya veri kaybına ve "dosya kullanımda" hatalarına yol açar; bu yüzden hep with kullan.
with open("notlar.txt", "w", encoding="utf-8") as f:
f.write("Anadolu Market günlük notlar\n")
f.write("Kulaklık stoğu kritik\n")
with open("notlar.txt", "r", encoding="utf-8") as f:
icerik = f.read()
print(icerik)
print("Karakter sayısı:", len(icerik))
Dosya modları
| Mod | Adı | Dosya varsa | Dosya yoksa |
|---|---|---|---|
"r" | okuma (read) | baştan okur | FileNotFoundError |
"w" | yazma (write) | içeriği siler, baştan yazar | oluşturur |
"a" | ekleme (append) | sonuna ekler | oluşturur |
"x" | yeni oluştur | FileExistsError | oluşturur |
"rb" / "wb" | ikili (binary) | resim, PDF gibi metin olmayan dosyalar; encoding verilmez | |
with open("islem.log", "w", encoding="utf-8") as f:
f.write("09:00 sistem açıldı\n")
with open("islem.log", "a", encoding="utf-8") as f:
f.write("09:15 sipariş 1001 alındı\n")
f.write("09:20 sipariş 1002 alındı\n")
with open("islem.log", encoding="utf-8") as f: # mod verilmezse "r"
print(f.read())
Satır satır okuma
read() tüm dosyayı tek metin olarak belleğe alır; milyonlarca satırlık bir log için uygun değildir. Dosya nesnesi üzerinde doğrudan for ile dönersen Python satırları teker teker okur. Her satırın sonunda \n gelir; strip() ile temizle.
with open("islem.log", encoding="utf-8") as f:
for numara, satir in enumerate(f, start=1):
satir = satir.strip()
if "sipariş" in satir:
print(f"{numara}: {satir}")
# Tüm satırları liste olarak almak
with open("islem.log", encoding="utf-8") as f:
satirlar = f.readlines()
print(len(satirlar), "satır")
print(satirlar[0].rstrip())
CSV'yi elle ayrıştırma
CSV (comma-separated values), her satırın bir kayıt, virgülle ayrılmış her parçanın bir alan olduğu düz metin dosyasıdır; Excel açar, her sistem üretir. İlk satır genelde başlık. Dosyadan okunan her şey metindir; sayısal alanları int/float ile dönüştürmeyi unutma.
csv_metni = """id,musteri,sehir,tutar
1001,Ayşe,Ankara,900.0
1002,Mehmet,İzmir,820.0
1003,Elif,Ankara,930.0
1004,Can,İstanbul,450.0
"""
with open("siparisler.csv", "w", encoding="utf-8") as f:
f.write(csv_metni)
siparisler = []
with open("siparisler.csv", encoding="utf-8") as f:
basliklar = f.readline().strip().split(",")
for satir in f:
satir = satir.strip()
if not satir: # boş satırı atla
continue
alanlar = satir.split(",")
kayit = dict(zip(basliklar, alanlar))
kayit["id"] = int(kayit["id"])
kayit["tutar"] = float(kayit["tutar"])
siparisler.append(kayit)
print(basliklar)
print(siparisler[0])
print("Toplam:", sum(s["tutar"] for s in siparisler))
dict(zip(basliklar, alanlar)) hilesi başlıkla değeri eşleştirip sözlük üretir; sütun sırası değişse bile kod bozulmaz. Alan içinde virgül ya da tırnak varsa (adres gibi) elle split yetmez; o zaman standart csv modülünü kullanırsın, orta seviyede göreceğiz.
Dosyaya rapor yazma
Önceki derste ürettiğin rapor metnini dosyaya yazmak tek satır: f.write(rapor). Birden çok satırı tek seferde yazmak için writelines var ama satır sonlarını kendin eklemelisin; en temizi "\n".join(...) ile metni hazırlayıp yazmaktır.
satirlar = [f"{'Şehir':<10}{'Ciro':>10}", "-" * 20]
for sehir, ciro in [("Ankara", 1830.0), ("İzmir", 820.0), ("İstanbul", 450.0)]:
satirlar.append(f"{sehir:<10}{ciro:>10,.2f}")
with open("rapor.txt", "w", encoding="utf-8") as f:
f.write("\n".join(satirlar) + "\n")
with open("rapor.txt", encoding="utf-8") as f:
print(f.read())
pathlib: dosya yolu bir nesnedir
os.path ile metin birleştirmek yerine pathlib.Path yolu bir nesne olarak taşır; / operatörüyle parçalar birleşir, işletim sistemi farkı (Windows'ta ters bölü) kendiliğinden çözülür. Küçük dosyalar için read_text ve write_text kısayolları with open yazmaktan da kısadır.
from pathlib import Path
klasor = Path("raporlar")
klasor.mkdir(exist_ok=True) # varsa hata verme
dosya = klasor / "ciro_2024_03.txt" # yol birleştirme
dosya.write_text("Ankara;1830.00\nİzmir;820.00\n", encoding="utf-8")
print(dosya.exists(), dosya.name, dosya.suffix, dosya.parent)
print(dosya.read_text(encoding="utf-8"))
for p in klasor.glob("*.txt"): # klasördeki txt dosyaları
print("bulundu:", p)
Derinleş: dosyalarla günlük iş
Temeli kurdun: open, with, modlar, satır satır okuma, pathlib. Bu bölümde iş hayatında her hafta karşına çıkacak işleri ele alıyoruz: "dosya bulunamadı" hatasının asıl nedeni, yolu parçalarına ayırmak, aynı anda iki dosyayla çalışmak, şubelerden gelen dosyaları birleştirmek ve Excel'den gelen bozuk görünen CSV'ler. İkili (binary) mod, seek, arşivler ve atomik yazmanın ayrıntısı orta seviyedeki İleri dosya işlemleri dersinde; csv ve json modülleri de JSON ve CSV dersinde.
Çalışma klasörü: göreli yol nereden başlar?
open("siparisler.csv") yazdığında Python dosyayı betiğin yanında değil, çalışma klasöründe (current working directory) arar. Terminalde hangi klasördeyken python komutunu verdiysen orasıdır. VS Code'da "Çalıştır" düğmesi çoğu zaman proje kökünü kullanır; bu yüzden "bende çalışıyordu" hatalarının yarısı buradan çıkar.
from pathlib import Path
import os
print("Çalışma klasörü:", Path.cwd())
print("Aynı bilgi os ile:", os.getcwd())
yol = Path("olmayan_rapor.csv")
print("Göreli yol:", yol)
print("Python aslında burada arayacak:", yol.resolve())
print("Var mı?", yol.exists())
Path.cwd() nerede? 2) Path(yol).resolve() tam olarak neyi gösteriyor? 3) Dosya adında gizli uzantı var mı (Windows "rapor.csv.txt" diye kaydetmiş olabilir)? Bu üçünü yazdırmak tahmin yürütmekten hızlıdır.Yolu parçalarına ayır: Path'in sorgu araçları
Dosya adlarıyla metin işlemi (split("."), replace) yapmak kırılgandır: "rapor.v2.csv" gibi adlar seni şaşırtır. Path nesnesi her parçayı ayrı özellik olarak verir.
| İfade | "veri/2026/satis_ekim.csv" için sonuç | Ne işe yarar? |
|---|---|---|
p.name | satis_ekim.csv | Klasörsüz tam dosya adı |
p.stem | satis_ekim | Uzantısız ad; rapor başlığı, sözlük anahtarı |
p.suffix | .csv | Uzantı (noktasıyla) |
p.parent | veri/2026 | Bulunduğu klasör |
p.with_suffix(".json") | veri/2026/satis_ekim.json | Aynı yerde farklı uzantılı çıktı |
p.with_name("ozet.txt") | veri/2026/ozet.txt | Aynı klasörde başka dosya |
p.is_file() / p.is_dir() | True / False | Var mı ve türü ne? |
p.stat().st_size | bayt cinsinden boyut | Boş dosyayı atlamak |
p.unlink(missing_ok=True) | dosyayı siler | Yoksa hata vermeden |
klasor.iterdir() / klasor.rglob("*.csv") | içerik / alt klasörler dahil arama | Toplu işleme |
from pathlib import Path
klasor = Path("veri") / "2026"
klasor.mkdir(parents=True, exist_ok=True) # ara klasörleri de oluştur
p = klasor / "satis_ekim.csv"
p.write_text("id,tutar\n1,250\n2,400\n", encoding="utf-8")
print(p.name, "|", p.stem, "|", p.suffix)
print("Klasör:", p.parent.as_posix())
print("JSON çıktısı:", p.with_suffix(".json").as_posix())
print("Boyut:", p.stat().st_size, "bayt")
for dosya in sorted(Path("veri").rglob("*.csv")):
print("CSV bulundu:", dosya.as_posix())
mkdir(parents=True, exist_ok=True) ikilisini ezberle: "ara klasörler de yoksa oluştur, varsa sessiz kal". as_posix() yolu her işletim sisteminde eğik çizgiyle (/) yazar; rapor ve log çıktısında tutarlı görünür.
Aynı anda iki dosya: oku, süz, yaz
Tek bir with satırında birden çok dosya açabilirsin. Kaynaktan satır satır okuyup hedefe yazmak, bellekte tüm dosyayı tutmadan "filtre" kurmanın standart yoludur. print(..., file=f) da dosyaya yazar ve satır sonunu kendisi ekler; write ise yalnız metin (str) alır ve \n eklemez.
with open("banka_islem.log", "w", encoding="utf-8") as f:
f.write("09:01 OK havale 1500\n09:04 HATA zaman aşımı\n09:10 OK eft 320\n09:12 HATA limit aşıldı\n")
hata_sayisi = 0
with open("banka_islem.log", encoding="utf-8") as kaynak, \
open("hatalar.log", "w", encoding="utf-8") as hedef:
for satir in kaynak:
if " HATA " in satir:
hedef.write(satir) # satırın kendi \n'i var
hata_sayisi += 1
print(f"Toplam {hata_sayisi} hata", file=hedef) # print satır sonunu ekler
print(open("hatalar.log", encoding="utf-8").read())
n = open("deneme.txt", "w", encoding="utf-8").write("Ankara\n")
print("write dönen değer (yazılan karakter):", n)
f.write(1500) → TypeError: write() argument must be str, not int. Önce str() ya da f-string kullan: f.write(f"{ciro:.2f}\n"). Ayrıca son örnekteki gibi with kullanmadan açılan dosya kapanmayabilir; o satır yalnızca dönen değeri göstermek için.Şubelerden gelen dosyaları birleştirmek
Anadolu Market'in her şubesi günlük satışını ayrı CSV olarak gönderiyor; hepsinin başlığı aynı. Birleştirirken başlığı yalnız bir kez yaz, boş satırları atla, kaynak dosyanın adını yeni bir sütun olarak ekle ki sonradan "bu satır hangi şubeden geldi?" sorusu cevaplanabilsin.
from pathlib import Path
gelen = Path("gelen_subeler")
gelen.mkdir(exist_ok=True)
(gelen / "kadikoy.csv").write_text("urun,adet\nkulaklik,3\nkablo,10\n", encoding="utf-8")
(gelen / "cankaya.csv").write_text("urun,adet\nkulaklik,5\n\n", encoding="utf-8")
cikti = ["sube,urun,adet"]
for dosya in sorted(gelen.glob("*.csv")):
satirlar = dosya.read_text(encoding="utf-8").splitlines()
for satir in satirlar[1:]: # başlığı atla
if satir.strip():
cikti.append(f"{dosya.stem},{satir}")
Path("tum_subeler.csv").write_text("\n".join(cikti) + "\n", encoding="utf-8")
print(Path("tum_subeler.csv").read_text(encoding="utf-8"))
Excel'den gelen CSV: BOM ve noktalı virgül
Türkçe Excel "CSV (UTF-8)" olarak kaydettiğinde iki sürpriz yapar: ayırıcı virgül değil noktalı virgüldür (çünkü ondalık ayırıcı virgüldür: 1250,50), ve dosyanın başına görünmez bir BOM (byte order mark, \ufeff) karakteri koyar. Sonuç: ilk sütunun adı "id" değil "\ufeffid" olur ve kayit["id"] KeyError verir.
with open("excel_cikti.csv", "w", encoding="utf-8-sig") as f: # Excel gibi BOM'lu yaz
f.write("id;musteri;tutar\n1;Ayşe;1250,50\n")
with open("excel_cikti.csv", encoding="utf-8") as f:
print("utf-8 ile ilk başlık:", repr(f.readline().split(";")[0]))
with open("excel_cikti.csv", encoding="utf-8-sig") as f:
basliklar = f.readline().strip().split(";")
alanlar = f.readline().strip().split(";")
kayit = dict(zip(basliklar, alanlar))
kayit["tutar"] = float(kayit["tutar"].replace(".", "").replace(",", "."))
print("utf-8-sig ile:", kayit)
| Belirti | Neden | Çözüm |
|---|---|---|
İlk sütun adı \ufeffid | BOM | encoding="utf-8-sig" |
| Her satır tek alan | Ayırıcı ; | split(";") ya da csv modülünde delimiter=";" |
float("1.250,50") ValueError | Türkçe sayı biçimi | Önce binlik noktayı sil, sonra virgülü noktaya çevir |
| "ÅŸ" gibi bozuk harfler | Dosya cp1254/latin ile yazılmış | encoding="cp1254" dene |
| Satırlar arası boş satır (Windows) | Çift satır sonu | csv modülüyle yazarken newline="" |
Basit ayar dosyası okumak
Küçük betikler ayarlarını çoğu zaman anahtar=değer satırlarından oluşan bir dosyada tutar. # ile başlayan satırlar yorumdur. Satırı yalnızca ilk eşittir işaretinden bölmek önemlidir; değerin içinde de = olabilir (split("=", 1)). Daha zengin biçimler (INI, TOML, .env) Yapılandırma dersinde.
glob), yeni dosyaları birleştirir, hatalı satırları ayrı bir dosyaya yazar ve işlenen dosyayı "islenmis" klasörüne taşır (rename). Bu iş akışının her parçasını bu derste gördün. İki altın kural: girdiyi asla yerinde değiştirme (yeni dosya üret) ve her çıktı dosyasının adına tarih koy (rapor_2026-10-28.txt), böylece dünkü rapor üzerine yazılmaz.Büyük dosyada bellek
Satır satır okuma, dosya ne kadar büyük olursa olsun bellekte yalnız bir satır tutar. Bir gigabaytlık log'u read() ile açmak programı çökertebilir; for satir in f ile dakikalar sürse de biter. Orta seviyedeki üreteçler (generator) bu fikri genelleştirir.
Yolu sabit yazma
"C:/Users/ayse/rapor.txt" gibi mutlak yollar başka bilgisayarda çalışmaz. Betiğin bulunduğu klasöre göreli yol kur: Path(__file__).parent / "veri" / "siparisler.csv". Ayarları ortam değişkeninden okumak da yaygındır.
Atomik yazma
Rapor yazılırken elektrik kesilirse yarım dosya kalır. Kurumsal betikler önce geçici dosyaya yazar, sonra Path.replace ile asıl adına taşır; taşıma işlemi ya tamamen olur ya hiç olmaz. Veri kaybının ucuz çözümü budur.