Python'da dosya okumak ve yazmak için yerleşik open(dosya, mod, encoding=...) fonksiyonu kullanılır ve en doğru yol onu with bloğuyla birlikte yazmaktır: with open("notlar.txt", "w", encoding="utf-8") as f: f.write("Merhaba"). Mod "r" ise dosya okunur, "w" ise sıfırdan yazılır, "a" ise sonuna eklenir. with bloğu bittiğinde dosya, arada hata çıksa bile otomatik olarak kapanır. Python 3.15 ile birlikte encoding verilmezse metin dosyaları her sistemde UTF-8 olarak açılır.

Bu yazıda open() modlarını tablo hâlinde, dosyayı tamamen ya da satır satır okumayı, metin ile ikili (binary) mod farkını, satır sonu dönüşümünü, karakter kodlaması seçimini ve Türkçe karakterlerde çıkan UnicodeDecodeError hatasını, seek() ile tell() kullanımını ve bellekte dosya gibi davranan io.StringIO nesnesini Python 3.15.2'de çalıştırılmış örneklerle anlatıyoruz. Dosya yollarıyla çalışmak için pathlib, with deyiminin iç işleyişi için with ve context manager yazılarına, tüm konular için Python öğrenme rehberi sayfasına bakabilirsiniz.

İlk örnek: dosyaya yazıp geri okumak

python
with open("notlar.txt", "w", encoding="utf-8") as f:
    adet = f.write("Birinci satır\n")
    f.write("İkinci satır: çğıöşü\n")

print(adet)

with open("notlar.txt", encoding="utf-8") as f:
    icerik = f.read()

print(icerik, end="")
print(f.closed)
# çıktı: 14
# çıktı: Birinci satır
# çıktı: İkinci satır: çğıöşü
# çıktı: True

open() bir dosya nesnesi (file object) döndürür. write() yazdığı karakter sayısını döndürür: "Birinci satır\n" 14 karakterdir, satır sonu karakteri de buna dahildir. write() sizin yerinize satır sonu eklemez; her satırın sonuna \n koymanız gerekir. İkinci open() çağrısında mod yazmadık; varsayılan mod "r", yani metin olarak okumadır. Son satırdaki f.closed değeri, with bloğundan çıkınca dosyanın kapandığını doğrular.

Resmî öğretici, with kullanmadan write() çağırıp dosyayı close() ile kapatmazsanız yazdıklarınızın, program başarıyla bitse bile diske tam olarak yazılmamış olabileceği konusunda uyarır. Kapanmış bir dosya nesnesini kullanmaya çalışırsanız da hata alırsınız:

python
f = open("notlar.txt", "w", encoding="utf-8")
f.write("deneme")
f.close()
try:
    f.write("tekrar")
except ValueError as e:
    print(e)
# çıktı: I/O operation on closed file.

open() modları

open() modları: Okuma (varsayılan, rt ile aynı), Yazma, Yalnızca yeni dosya oluşturma, Sona ekleme

İkinci argüman olan mod, dosyanın nasıl açılacağını birkaç harfle anlatır. Bir okuma ya da yazma harfi seçilir, isteğe bağlı olarak b (ikili) ve + (hem okuma hem yazma) eklenir. Belgelerdeki tablonun özeti şöyledir:

ModAnlamıDosya yoksaDosya varsa
rOkuma (varsayılan, rt ile aynı)FileNotFoundErrorBaştan okunur
wYazmaOluşturulurİçeriği silinir
xYalnızca yeni dosya oluşturmaOluşturulurFileExistsError
aSona eklemeOluşturulurSonuna yazılır
r+Okuma ve yazmaFileNotFoundErrorSilinmez
w+Yazma ve okumaOluşturulurİçeriği silinir
b eki (rb, wb, ab)İkili mod, bytes ile çalışırModa göreModa göre
t ekiMetin modu (varsayılan)Moda göreModa göre

En sık yapılan pahalı hata, var olan bir dosyayı yanlışlıkla "w" ile açıp içeriğini silmektir. Var olan bir dosyanın üzerine yazmamak istiyorsanız "x" modu güvenlidir; dosya zaten varsa hiçbir şey yazmadan hata verir:

python
with open("rapor.txt", "x", encoding="utf-8") as f:
    f.write("ilk sürüm\n")

try:
    with open("rapor.txt", "x", encoding="utf-8") as f:
        f.write("ikinci sürüm\n")
except FileExistsError as e:
    print(e)

try:
    open("olmayan.txt", encoding="utf-8")
except FileNotFoundError as e:
    print(e)
    print(e.errno, e.filename)
# çıktı: [Errno 17] File exists: 'rapor.txt'
# çıktı: [Errno 2] No such file or directory: 'olmayan.txt'
# çıktı: 2 olmayan.txt

Her iki hata da OSError'ın alt sınıfıdır ve errno, strerror, filename gibi özellikler taşır. Bunları nasıl yakalayacağınızı Python try except yazısında anlattık. "a" modu ise mevcut içeriğe dokunmadan sona yazar; günlük (log) dosyaları için tipik seçimdir:

python
for satir in ["09:00 başladı", "09:05 bitti"]:
    with open("gunluk.txt", "a", encoding="utf-8") as f:
        f.write(satir + "\n")

with open("gunluk.txt", encoding="utf-8") as f:
    print(f.read(), end="")
# çıktı: 09:00 başladı
# çıktı: 09:05 bitti

Okuma yöntemleri: read, readline, döngü

Dosya nesnesinin birkaç okuma yolu vardır ve her birinin bellek kullanımı farklıdır. read() argümansız çağrılırsa dosyanın tamamını tek bir metin olarak döndürür; öğreticinin deyişiyle dosya belleğinizin iki katıysa bu sizin sorununuzdur. read(n) en fazla n karakter (ikili modda n bayt) okur. Dosyanın sonuna gelindiğinde read() boş metin ('') döndürür. readline() tek bir satır okur ve sondaki \n karakterini korur; böylece boş bir satır '\n', dosya sonu ise '' olarak ayırt edilebilir.

python
with open("sehirler.txt", "w", encoding="utf-8") as f:
    f.write("İstanbul\nAnkara\n\nİzmir\n")

with open("sehirler.txt", encoding="utf-8") as f:
    print(repr(f.readline()))
    print(repr(f.read(3)))
    print(repr(f.readline()))
    print(repr(f.readline()))
    print(repr(f.read()))
    print(repr(f.read()))
# çıktı: 'İstanbul\n'
# çıktı: 'Ank'
# çıktı: 'ara\n'
# çıktı: '\n'
# çıktı: 'İzmir\n'
# çıktı: ''

Okuma işlemleri dosyada kaldığı yerden devam eder: read(3) sonrasındaki readline() satırın kalan kısmını getirdi. Büyük dosyaları satır satır işlemenin en verimli ve en okunur yolu dosya nesnesinin üzerinde doğrudan for döngüsü kurmaktır; Python her adımda yalnızca bir satırı belleğe alır. Satır sonu karakterinden kurtulmak için rstrip("\n") ya da strip() kullanılır:

python
with open("sehirler.txt", "w", encoding="utf-8") as f:
    f.write("İstanbul\nAnkara\n\nİzmir\n")

with open("sehirler.txt", encoding="utf-8") as f:
    for no, satir in enumerate(f, start=1):
        satir = satir.rstrip("\n")
        if not satir:
            continue
        print(no, satir)

with open("sehirler.txt", encoding="utf-8") as f:
    print(f.readlines())
# çıktı: 1 İstanbul
# çıktı: 2 Ankara
# çıktı: 4 İzmir
# çıktı: ['İstanbul\n', 'Ankara\n', '\n', 'İzmir\n']

Tüm satırları bir listede istiyorsanız readlines() ya da list(f) kullanabilirsiniz, ancak bunlar dosyanın tamamını belleğe alır. Satırları sonlarındaki \n olmadan almak için f.read().splitlines() pratik bir alternatiftir. Yazma tarafında writelines(liste) listedeki metinleri arka arkaya yazar ama yine satır sonu eklemez. print() fonksiyonunun file parametresi ise satır sonunu sizin için ekler ve metin olmayan değerleri de dönüştürür:

python
urunler = [("kalem", 12), ("defter", 3)]
with open("stok.txt", "w", encoding="utf-8") as f:
    f.writelines(["ÜRÜN STOK\n", "---------\n"])
    for ad, adet in urunler:
        print(ad, adet, sep=";", file=f)

with open("stok.txt", encoding="utf-8") as f:
    print(f.read().splitlines())
# çıktı: ['ÜRÜN STOK', '---------', 'kalem;12', 'defter;3']

Noktalı virgülle ayrılmış veriyi elle yazmak yerine tırnak ve ayırıcı kurallarını sizin yerinize uygulayan csv modülünü, yapılandırılmış veri için json modülünü kullanmanız daha güvenlidir.

Karakter kodlaması: encoding ve Python 3.15 değişikliği

Metin modunda dosyadaki baytlar bir karakter kodlamasına göre metne çevrilir. Python 3.15'e kadar encoding verilmediğinde kullanılan kodlama işletim sistemine bağlıydı; Türkçe Windows sistemlerde bu genellikle cp1254 olurdu ve aynı kod Linux sunucuda UTF-8 ile çalışırdı. Python 3.15 PEP 686'yı uygulayarak UTF-8 modunu varsayılan yaptı: artık open("dosya.txt") her sistemde UTF-8 kullanır. Aşağıdaki örneği -X utf8 gibi bir seçenek vermeden, düz python main.py ile Türkçe bir Windows sistemde çalıştırdık:

python
import locale
import sys

print(sys.flags.utf8_mode)
print(locale.getencoding())
with open("varsayilan.txt", "w") as f:
    print(f.encoding)
# çıktı: 1
# çıktı: cp1254
# çıktı: utf-8

Sistemin yerel kodlaması hâlâ cp1254 ama dosya UTF-8 ile açıldı. Yine de 3.15 belgeleri, farklı Python sürümleri arasında aynı sonucu almak için encoding argümanını her zaman açıkça yazmanızı önerir. Eski davranışa ihtiyacınız varsa iki yol vardır: tek bir dosya için encoding="locale" (Python 3.10'dan beri desteklenir) ya da tüm program için PYTHONUTF8=0 ortam değişkeni veya -X utf8=0 seçeneği. Kodunuzda kodlama verilmemiş open() çağrılarını bulmak için -X warn_default_encoding seçeneğiyle çalıştırırsanız Python her birinde bir EncodingWarning basar:

python
# main.py
open("bos.txt", "w").close()
bash
python -X warn_default_encoding main.py
# main.py:2: EncodingWarning: 'encoding' argument not specified
#   open("bos.txt", "w").close()

UnicodeDecodeError ve errors parametresi

Bir dosyayı yazıldığı kodlamadan farklı bir kodlamayla okursanız UnicodeDecodeError alırsınız. Bu hata ValueError'ın alt sınıfıdır. Örnekte "Şeker" kelimesini eski Windows Türkçe kodlamasıyla (cp1254) bayt olarak yazıp UTF-8 ile okumaya çalışıyoruz:

python
with open("eski.txt", "wb") as f:
    f.write("Şeker".encode("cp1254"))

try:
    with open("eski.txt", encoding="utf-8") as f:
        f.read()
except UnicodeDecodeError as e:
    print(e)

with open("eski.txt", encoding="cp1254") as f:
    print(f.read())
with open("eski.txt", encoding="utf-8", errors="replace") as f:
    print(f.read())
# çıktı: 'utf-8' codec can't decode byte 0xde in position 0: invalid continuation byte
# çıktı: Şeker
# çıktı: �eker

Doğru çözüm dosyanın gerçek kodlamasını bilip onu vermektir; burada encoding="cp1254" metni bozulmadan okudu. errors parametresi ise çözülemeyen baytlar karşısında ne yapılacağını belirler. Varsayılan "strict" hata fırlatır, "replace" bozuk baytın yerine bir yedek işaret koyar (yukarıdaki �), "ignore" o baytı atlar, "backslashreplace" ise onu \xde gibi bir kaçış dizisiyle gösterir. "replace" ve "ignore" veri kaybettirir; bunları yalnızca ne kaybettiğinizi bildiğiniz durumlarda kullanın. Kodlama kavramlarının ayrıntısı ve Türkçe İ/ı sorunları için Python Unicode ve Türkçe karakter yazısına bakabilirsiniz.

codecs belgesine göre Microsoft, Not Defteri (Notepad) programı için UTF-8 dosyaların başına görünmez bir imza (BOM, EF BB BF baytları) ekleyen bir varyant geliştirdi; bu tür dosyalarla sık karşılaşırsınız. Bu dosyayı encoding="utf-8" ile okursanız metnin başında \ufeff karakteri belirir ve ilk sütun adı beklediğiniz değerle eşleşmez. codecs belgesinde anlatılan "utf-8-sig" kodlaması okurken bu imzayı atlar, yazarken ekler:

python
with open("excel.csv", "w", encoding="utf-8-sig") as f:
    f.write("ad;soyad\n")

with open("excel.csv", encoding="utf-8") as f:
    print(repr(f.read()))
with open("excel.csv", encoding="utf-8-sig") as f:
    print(repr(f.read()))
# çıktı: '\ufeffad;soyad\n'
# çıktı: 'ad;soyad\n'

Metin modu ile ikili mod

Metin modu ile ikili mod: Okunan tür, encoding / errors, Satır sonu dönüşümü, read(n) birimi

Moda b eklediğinizde dosya ikili modda açılır: okunan veri bytes olarak gelir, hiçbir kodlama çözümlemesi ve satır sonu dönüşümü yapılmaz. Resim, PDF, sıkıştırılmış arşiv gibi metin olmayan dosyalar her zaman ikili modda açılmalıdır; öğretici, metin modundaki satır sonu dönüşümünün JPEG ya da EXE dosyalarını bozacağı konusunda açıkça uyarır. İkili modda encoding verilemez. Akışlar veri türü konusunda da katıdır: metin dosyasına bytes, ikili dosyaya str yazmak TypeError üretir:

python
try:
    with open("veri.bin", "wb") as f:
        f.write("metin")
except TypeError as e:
    print(e)

try:
    with open("veri.txt", "w", encoding="utf-8") as f:
        f.write(b"bayt")
except TypeError as e:
    print(e)
# çıktı: a bytes-like object is required, not 'str'
# çıktı: write() argument must be str, not bytes

open() moda göre farklı sınıflardan nesneler döndürür: metin modunda io.TextIOWrapper, ikili okumada io.BufferedReader, ikili yazma ve eklemede io.BufferedWriter. Bunların ortak davranışı io modülünde tanımlıdır.

ÖzellikMetin modu (r, w, a)İkili mod (rb, wb, ab)
Okunan türstrbytes
encoding / errorsKullanılırVerilemez
Satır sonu dönüşümüVar (newline ile ayarlanır)Yok
read(n) birimiKarakterBayt
seek() sınırıYalnızca tell() değerleri veya 0Her konum
Uygun dosyalartxt, csv, json, kodResim, PDF, zip, exe

Satır sonları ve newline parametresi

Metin modunda okurken Python varsayılan olarak evrensel satır sonu (universal newlines) modunu kullanır: dosyada \n, \r\n ya da \r olsun, size hep \n olarak gelir. Yazarken ise \n karakterleri işletim sisteminin satır sonuna (os.linesep) çevrilir; Windows'ta bu \r\n demektir. newline="" verirseniz yazarken hiçbir dönüşüm yapılmaz. Aşağıdaki örnek Windows'ta çalıştırıldı; Linux ve macOS'ta iki satır da b'a\nb\n' yazar:

python
with open("satir.txt", "w", encoding="utf-8") as f:
    f.write("a\nb\n")
with open("satir.txt", "rb") as f:
    print(f.read())

with open("satir.txt", "w", encoding="utf-8", newline="") as f:
    f.write("a\nb\n")
with open("satir.txt", "rb") as f:
    print(f.read())
# çıktı: b'a\r\nb\r\n'
# çıktı: b'a\nb\n'

Bu ayrıntı en çok CSV dosyalarında önem kazanır; csv modülü belgeleri dosyanın newline="" ile açılmasını ister.

seek() ve tell(): dosya içinde konum

tell() dosya nesnesinin o anki konumunu, seek(ofset, nereden) ise yeni konumu ayarlar. nereden 0 ise baştan, 1 ise mevcut konumdan, 2 ise dosya sonundan sayılır. İkili modda konum bayt cinsindendir ve her türlü atlama serbesttir:

python
with open("sayilar.bin", "wb+") as f:
    f.write(b"0123456789abcdef")
    print(f.seek(5))
    print(f.read(1))
    print(f.seek(-3, 2))
    print(f.read(1))
    print(f.tell())
# çıktı: 5
# çıktı: b'5'
# çıktı: 13
# çıktı: b'd'
# çıktı: 14

Metin modunda kurallar daha sıkıdır: yalnızca baştan itibaren atlamaya izin verilir (tek istisna seek(0, 2) ile dosya sonuna gitmektir) ve geçerli ofsetler yalnızca tell()'in döndürdüğü değerler ya da sıfırdır. Bunun nedeni, UTF-8'de bir karakterin birden fazla bayt tutabilmesidir; metin modundaki tell() değeri karakter sayısı değil, Python'un iç kullanımına yönelik bir sayıdır.

Bellekte dosya: io.StringIO

Bir fonksiyon dosya nesnesi bekliyorsa ve siz gerçek bir dosya oluşturmak istemiyorsanız (örneğin bir test yazarken), io.StringIO bellekte metin dosyası gibi davranan bir nesne sağlar. İkili veri için karşılığı io.BytesIO'dur:

python
import io

def satir_say(dosya):
    return sum(1 for satir in dosya if satir.strip())

sahte = io.StringIO("bir\niki\n\nüç\n")
print(satir_say(sahte))

cikti = io.StringIO()
print("rapor hazır", file=cikti)
print(repr(cikti.getvalue()))
# çıktı: 3
# çıktı: 'rapor hazır\n'

Uygulama: günlük dosyasından hataları ayıklamak

Şimdi öğrendiklerimizi birleştirelim. Bir uygulama günlüğünden yalnızca ERROR içeren satırları ayrı bir dosyaya yazan ve kaç satır bulduğunu raporlayan küçük bir betik yazıyoruz. İki dosya aynı with satırında açılıyor; okuma satır satır yapıldığı için günlük ne kadar büyük olursa olsun bellek kullanımı düşük kalır:

python
with open("uygulama.log", "w", encoding="utf-8") as f:
    f.write("INFO açılış\nERROR veritabanı yok\nINFO istek\nERROR zaman aşımı\n")

bulunan = 0
with (open("uygulama.log", encoding="utf-8") as kaynak,
      open("hatalar.log", "w", encoding="utf-8") as hedef):
    for satir in kaynak:
        if satir.startswith("ERROR"):
            hedef.write(satir)
            bulunan += 1

print(f"{bulunan} hata satırı bulundu")
with open("hatalar.log", encoding="utf-8") as f:
    print(f.read(), end="")
# çıktı: 2 hata satırı bulundu
# çıktı: ERROR veritabanı yok
# çıktı: ERROR zaman aşımı

Parantezli çoklu with yazımı Python 3.10'dan beri desteklenir. Gerçek bir uygulamada bu tür günlükleri elle yazmak yerine logging modülünü kullanmanızı öneririz.

Sık yapılan hatalar

Sık yapılan hatalar: encoding vermemek, "w" ile var olan dosyayı silmek, Dosyayı kapatmamak, write() satır sonu ekler sanmak
  • encoding vermemek: 3.15 öncesi sürümlerde kod Windows'ta cp1254, Linux'ta UTF-8 ile çalışır ve Türkçe karakterler bir sistemde bozulur. Her open() çağrısına encoding="utf-8" yazın.
  • "w" ile var olan dosyayı silmek: "w" dosyayı açtığı anda boşaltır; eklemek için "a", üzerine yazmamak için "x" kullanın.
  • Dosyayı kapatmamak: with kullanmazsanız yazılanlar diske ulaşmayabilir ve dosya tanıtıcıları tükenebilir.
  • write() satır sonu ekler sanmak: write() ve writelines() \n eklemez; print(..., file=f) ekler.
  • Büyük dosyayı read() ile okumak: Dosyanın tamamı belleğe alınır; satır satır döngü ya da read(n) ile parça parça okuyun.
  • İkili dosyayı metin modunda açmak: Kod çözme hatası alırsınız ya da satır sonu dönüşümü veriyi bozar; resim ve arşivler için "rb"/"wb" kullanın.
  • Göreli yolun nereye göre olduğunu unutmak: open("veri.txt") betiğin klasörüne değil, programın çalıştırıldığı geçerli klasöre göre çözülür. Betiğin yanındaki dosyayı açmak için pathlib ile Path(__file__).parent / "veri.txt" kullanabilirsiniz.

Sık Sorulan Sorular

Python'da dosyaya satır nasıl eklenir?

Dosyayı "a" moduyla açın ve satırın sonuna \n koyarak yazın: with open("dosya.txt", "a", encoding="utf-8") as f: f.write("yeni satır\n"). Dosya yoksa oluşturulur.

Python 3.15'te encoding yazmak hâlâ gerekli mi?

3.15'te varsayılan zaten UTF-8 olduğu için sonuç aynıdır, ancak belgeler kodunuzun eski sürümlerde de aynı davranması için encoding argümanını açıkça yazmanızı önerir. Dosya UTF-8 değilse (örneğin cp1254 ya da BOM içeren UTF-8) doğru kodlamayı her sürümde belirtmeniz gerekir.

Dosyanın var olup olmadığını nasıl kontrol ederim?

pathlib.Path("dosya.txt").exists() ya da os.path.exists() kullanabilirsiniz. Ancak kontrol ile açma arasında dosya silinebileceği için çoğu durumda doğrudan açıp FileNotFoundError yakalamak daha güvenlidir.

Dosyayı hem okuyup hem yazabilir miyim?

Evet, "r+" mevcut dosyayı silmeden okuma ve yazma için, "w+" ise dosyayı boşaltarak açar. Okuma ile yazma arasında konumu seek() ile ayarlamayı unutmayın. Çoğu durumda dosyayı okuyup içeriği değiştirdikten sonra ayrı bir "w" işlemiyle yazmak daha az hataya açıktır.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: Python öğrenme rehberi · Python pathlib · Python with ve context manager · Python Unicode ve Türkçe karakter · Python csv modülü · Python json modülü · Python try except