---
title: 'Dosya İşlemleri'
source: 'https://academia.sh/tr/kurslar/python-temelleri/dosya-islemleri'
course: 'Python Temelleri'
language: tr
updated: '2026-08-17T18:10:27+00:00'
license: 'CC BY-SA 4.0'
---

# Dosya İşlemleri

Aynı 41 bayt ikili kipte 41 uzunluğunda bir bayt dizisi, metin kipinde utf-8 ile 34 karakterlik bir dizgi verir; yanlış kodlama açma değil okuma anında kırılır ve latin-1 hiç kırılmayıp sessizce 41 yanlış karakter üretir.

Önceki dersin on iki kaydı bellekte hazır duruyordu. `deger` alanı zaten bir dizgiydi; tek iş
onu sayıya çevirmek ve alanın sınırlarına vurmaktı. Alan kökü işe yaradı çünkü hatanın
kaynağı alanın kendisiydi.

Kayıtlar bir dosyadan geldiğinde araya bir katman daha girer. Dosyada duran şey ne dizgidir ne
sayı — **bayttır**. Baytın dizgiye çevrilmesi bir **kodlama** kararıdır ve o karar yanlışsa
hata alan katmanına hiç ulaşmaz; daha okuma sırasında ortaya çıkar. Dosya, kodlama ve bayt
kavramları Bilgisayarlar Nasıl Çalışır kursunda kuruldu; orada **kavram** kuruldu, burada
Python'un iki dosya kipinin aynı baytlarda ne verdiği ölçülüyor.

## İki Kip, İki Tür

`open` iki kipte çalışır ve aralarındaki fark bir seçenek değil, **dönen nesnenin türüdür**.

**İkili kip** (`"rb"`, `"wb"`) bayt dizisi verir. Dosyada ne varsa odur; hiçbir çeviri yapılmaz.
Uzunluk **bayt sayısıdır**.

**Metin kipi** (`"r"`, `"w"`) dizgi verir. Araya bir çözücü girer: baytlar bir **kodlamaya**
göre karakterlere çevrilir ve satır sonları çevrilir. Uzunluk **karakter sayısıdır**.

Metin kipinde `encoding` verilmezse yorumlayıcı bir öntanımlı seçer ve o öntanımlı **çalıştığı
ortama bağlıdır**. Aynı dosya, aynı program, başka bir ortam — başka sonuç. Bu dersin bütün
ölçümleri kodlamayı açıkça yazar; ölçülebilir olmasının koşulu budur.

## Aynı Baytlar, Ayrı Kodlamalar

Ölçüm tek bir metinle başlar: Türkçe harfler, bir derece işareti ve bir uzun çizgi içeren bir
ölçüm satırı. Bu metin utf-8 ile kodlanır ve elde edilen baytlar **beş ayrı kodlamayla**
çözülmeye çalışılır.

Ölçümün varsayımları:

- **HD19** — Bütün ölçüm tek bir bayt dizisi üzerinde yapılır; dizi metinden **kodlanarak**
  üretilir, elle yazılmaz.
- **HD20** — Kâhin kaynak metnin kendisidir: çözülen dizgi kaynakla **özdeş** mi diye
  sorulabilir, çünkü kaynağı biz ürettik.
- **HD21** — "Kırılan bayt konumu" istisna nesnesinin kendi alanından okunur; hata iletisi
  ayrıştırılmaz.
- **HD22** — Kodlama adları dilin kendi çözücü kataloğundan gelir; hiçbiri dış bir araca ait
  değildir.
- **HD23** — Parça boyu taraması aynı baytları farklı boylarda böler ve her parçayı **ayrı
  ayrı** çözer; parçalar arasında durum taşınmaz.

```python
"""Ayni baytlar, ayri kodlamalar: cozulur mu, ozdes mi, nerede kirilir."""

METIN = "kuzey yamaç: 12,5 °C — ölçüm tamam"
HAM = METIN.encode("utf-8")

print(f"karakter {len(METIN)} | utf-8 bayt {len(HAM)} | "
      f"çok baytlı karakter {sum(len(k.encode('utf-8')) > 1 for k in METIN)}")

KODLAMALAR = ("utf-8", "utf-16", "latin-1", "ascii", "cp1254")

print()
print(f"{'kodlama':<12s}{'çözüldü':<10s}{'karakter':>9s}{'özdeş':>8s}"
      f"   kırılan sınıf / bayt konumu")
for ad in KODLAMALAR:
    try:
        cozulen = HAM.decode(ad)
    except UnicodeDecodeError as e:
        print(f"  {ad:<10s}{'hayır':<10s}{'-':>9s}{'-':>8s}"
              f"   {type(e).__name__} / {e.start}")
    else:
        print(f"  {ad:<10s}{'evet':<10s}{len(cozulen):>9d}"
              f"{('evet' if cozulen == METIN else 'hayır'):>8s}   -")

print()
print(f"{'hata kipi':<20s}{'karakter':>9s}{'özdeş':>8s}   ilk yedi karakter")
for kip in ("ignore", "replace", "backslashreplace"):
    c = HAM.decode("ascii", errors=kip)
    print(f"  {kip:<18s}{len(c):>9d}{('evet' if c == METIN else 'hayır'):>8s}"
          f"   {c[:7]!r}")

print()
print(f"{'parça boyu':<12s}{'parça':>7s}{'kırılan parça':>15s}")
for boy in range(4, 25, 2):
    parcalar = [HAM[i:i + boy] for i in range(0, len(HAM), boy)]
    kirilan = 0
    for p in parcalar:
        try:
            p.decode("utf-8")
        except UnicodeDecodeError:
            kirilan += 1
    print(f"  {boy:<10d}{len(parcalar):>7d}{kirilan:>15d}")
butun = 0
try:
    HAM.decode("utf-8")
except UnicodeDecodeError:
    butun = 1
print(f"aynı baytlar bütün olarak çözüldüğünde kırılan {butun}")
```

```
karakter 34 | utf-8 bayt 41 | çok baytlı karakter 6

kodlama     çözüldü    karakter   özdeş   kırılan sınıf / bayt konumu
  utf-8     evet             34    evet   -
  utf-16    hayır             -       -   UnicodeDecodeError / 40
  latin-1   evet             41   hayır   -
  ascii     hayır             -       -   UnicodeDecodeError / 10
  cp1254    evet             41   hayır   -

hata kipi            karakter   özdeş   ilk yedi karakter
  ignore                   28   hayır   'kuzey y'
  replace                  41   hayır   'kuzey y'
  backslashreplace         80   hayır   'kuzey y'

parça boyu    parça  kırılan parça
  4              11              4
  6               7              2
  8               6              2
  10              5              2
  12              4              2
  14              3              2
  16              3              0
  18              3              0
  20              3              2
  22              2              0
  24              2              2
aynı baytlar bütün olarak çözüldüğünde kırılan 0
```

## Sessiz Yanlış

İlk satır dersin çekirdeğini veriyor: **34** karakterlik metin **41** bayt tutuyor. Aradaki
**7** baytlık fark, çok baytlı **6** karakterden geliyor. Karakter sayısı ile bayt sayısı
birbirinden bağımsızdır ve hangisini saydığınız hangi kipte okuduğunuza bağlıdır.

Kodlama tablosu üç ayrı davranış gösteriyor. **utf-8** çözüyor, **34** karakter veriyor ve
kaynakla **özdeş**. Doğru kodlamanın tanımı budur.

**ascii** ve **utf-16** çözemiyor ve `UnicodeDecodeError` fırlatıyor. Konumlar ayrı:
ascii **10**'uncu baytta kırılıyor — ilk Türkçe harfin ilk baytında. utf-16 ise **40**'ıncı
baytta kırılıyor, yani neredeyse sonda; çünkü utf-16 baytları ikişerli okur ve **41** tek
sayıdır, son bayt eşsiz kalır. İkisi de kırılır ama **nerede** kırıldıkları kodlamanın
kendi kuralından gelir.

Asıl bulgu **latin-1** ve **cp1254** satırlarıdır. İkisi de **çözüyor** — hiç istisna
fırlatmıyor — ve **41** karakter veriyor. Yani her baytı bir karaktere çeviriyorlar. Ama
**özdeş değil**. Tek baytlı bir kodlama, önüne gelen her baytı geçerli sayar; kırılacak bir
şey yoktur. Bu, ölçümün en tehlikeli sonucudur: **yanlış kodlama her zaman istisna
üretmez.** İstisna fırlatan kodlama sizi uyarır; fırlatmayan kodlama sessizce yanlış metin
verir ve yanlışlık ancak metni okuyan insan tarafından fark edilir.

Hata kipleri bunun bilinçli hâlidir. `ignore` çözemediği baytları atıyor ve **28** karakter
kalıyor — **6** karakterin baytları düşmüş. `replace` her kusurlu baytın yerine bir karakter
koyuyor ve **41** çıkıyor; sayı bayt sayısına eşitleniyor çünkü değiştirme bayt başına
yapılıyor. `backslashreplace` her kusurlu baytı dört karakterlik bir yazımla anlatıyor ve
uzunluk **80**'e çıkıyor. Üçünün de `özdeş` sütunu **hayır**: hata kipi vermek, veriyi
kurtarmaz, yalnız **kırılmayı** engeller.

## Parçanın Sınırı

Son tablo ikili kipte parça parça okumanın bedelini veriyor. Aynı **41** bayt, farklı
boylarda bölünüp her parça **ayrı ayrı** çözülüyor.

Sonuç düzensiz. **4** baytlık parçalarda **11** parçanın **4**'ü kırılıyor; **16** ve **18**
boyunda hiçbiri kırılmıyor; **20**'de yine **2** parça kırılıyor, **22**'de **0**, **24**'te
**2**. Kırılma sayısı parça boyunun büyüklüğüne bağlı değil, parça sınırının **çok baytlı bir
karakterin ortasına düşüp düşmediğine** bağlı. Aynı baytlar **bütün olarak** çözüldüğünde
kırılan **0**.

Bunun doğrudan sonucu şudur: ikili kipte okuyup baytları kendiniz çözüyorsanız, parçalar
arasında **durum taşımak** sizin işinizdir. Metin kipi bu işi zaten yapar — çözücü parça
sınırında yarım kalan karakteri bekletir. İkili kip hızlı ve tarafsızdır; çeviriyi
üstlenmediği için çevirinin sorunlarını da üstlenmez ve onları çağırana bırakır.

## Kip, Tür ve Kırılma Noktası

İkinci ölçüm gerçek bir dosyayla çalışır. Dosya **geçici bir dizinde** kurulur ve ölçüm
bitince dizin silinir; ölçüm hiçbir kalıcı iz bırakmaz.

- **HD24** — Dosya geçici bir dizinde üretilir ve baytları önceki ölçümün metninden gelir;
  hiçbir yol adı basılmaz, çünkü yol ortama bağlıdır.
- **HD25** — "Uzunluk" ikili kipte bayt, metin kipinde karakter sayısıdır; ikisi aynı adla
  ölçülür ama aynı şey değildir.
- **HD26** — Kırılma noktası ölçümü açma ile okumayı **ayrı adım** olarak dener; hangisinin
  istisna ürettiği koşumdan okunur.
- **HD27** — Satır sonu ölçümünde dosya, biri iki karakterlik biri tek karakterlik olmak
  üzere **iki ayrı satır sonu biçimi** taşır; çeviri farkı bundan doğar.

```python
"""Metin kipi ile ikili kip: ayni dosya, ayri tur, ayri uzunluk, ayri kirilma noktasi."""
import tempfile
from pathlib import Path

METIN = "kuzey yamaç: 12,5 °C — ölçüm tamam"
SATIRLI = b"kuzey\r\nyama\xc3\xa7\nolcum\r\n"

with tempfile.TemporaryDirectory() as gecici:
    olcum = Path(gecici) / "olcum.dat"
    olcum.write_bytes(METIN.encode("utf-8"))
    satir = Path(gecici) / "satir.dat"
    satir.write_bytes(SATIRLI)

    print(f"{'okuma kipi':<22s}{'tür':<8s}{'uzunluk':>8s}{'özdeş':>8s}")
    with open(olcum, "rb") as d:
        ikili = d.read()
    print(f"  {'ikili':<20s}{type(ikili).__name__:<8s}{len(ikili):>8d}{'-':>8s}")
    for ad, kodlama, kip in (("metin utf-8", "utf-8", "strict"),
                             ("metin latin-1", "latin-1", "strict"),
                             ("metin ascii replace", "ascii", "replace")):
        with open(olcum, "r", encoding=kodlama, errors=kip) as d:
            metin = d.read()
        print(f"  {ad:<20s}{type(metin).__name__:<8s}{len(metin):>8d}"
              f"{('evet' if metin == METIN else 'hayır'):>8s}")

    print()
    print("adım                sonuç")
    try:
        d = open(olcum, "r", encoding="ascii")
    except UnicodeDecodeError as e:
        print(f"  {'open':<18s}{type(e).__name__} / bayt {e.start}")
    else:
        print(f"  {'open':<18s}başarılı, dosya açıldı")
        try:
            d.read()
        except UnicodeDecodeError as e:
            print(f"  {'read':<18s}{type(e).__name__} / bayt {e.start}")
        finally:
            d.close()

    print()
    print(f"{'okuma biçimi':<22s}{'uzunluk':>8s}{'CR sayısı':>11s}{'satır':>7s}")
    with open(satir, "rb") as d:
        ham = d.read()
    print(f"  {'ikili':<20s}{len(ham):>8d}{ham.count(13):>11d}"
          f"{len(ham.splitlines()):>7d}")
    for ad, yenisatir in (("metin (öntanımlı)", None), ('metin newline=""', "")):
        with open(satir, "r", encoding="utf-8", newline=yenisatir) as d:
            m = d.read()
        with open(satir, "r", encoding="utf-8", newline=yenisatir) as d:
            satirlar = d.readlines()
        print(f"  {ad:<20s}{len(m):>8d}{m.count(chr(13)):>11d}"
              f"{len(satirlar):>7d}")

print("geçici dizin silindi:", not Path(gecici).exists())
```

```
okuma kipi            tür      uzunluk   özdeş
  ikili               bytes         41       -
  metin utf-8         str           34    evet
  metin latin-1       str           41   hayır
  metin ascii replace str           41   hayır

adım                sonuç
  open              başarılı, dosya açıldı
  read              UnicodeDecodeError / bayt 10

okuma biçimi           uzunluk  CR sayısı  satır
  ikili                     21          2      3
  metin (öntanımlı)         18          0      3
  metin newline=""          20          2      3
geçici dizin silindi: True
```

## Sayıların Okunması

Birinci tablo iki kipin farkını türle veriyor. İkili kip `bytes` döndürüyor ve uzunluk
**41**; metin kipi `str` döndürüyor ve doğru kodlamayla uzunluk **34**. Aynı dosya, aynı
içerik, iki ayrı sayı — ve ikisi de doğru. `latin-1` yine **41** veriyor ve `özdeş` sütunu
`hayır`: bellekteki ölçümde görülen sessiz yanlış, dosyadan okurken de aynen tekrarlanıyor.

İkinci tablo dersin en kesin bulgusudur. `open` çağrısı `ascii` kodlamasıyla **başarılı
oluyor** — dosya açılıyor, hiçbir istisna çıkmıyor. İstisna `read` çağrısında, **10**'uncu
baytta çıkıyor. `open` yalnız dosyayı hazırlar ve kodlama adını saklar; çözme işi ilk okumada
yapılır. Kodlama hatası bir **açma hatası değil, okuma hatasıdır** ve bunun pratik sonucu
şudur: `open` çağrısını saran bir `try` bloğu bu hatayı yakalayamaz. Yakalayacak blok okumayı
sarmalıdır.

Üçüncü tablo satır sonu çevirisini sayıyor. Dosya **21** bayt ve içinde **2** tane satır başı
karakteri var, çünkü iki satır iki karakterlik satır sonuyla, biri tek karakterlik satır
sonuyla bitiyor. Metin kipi öntanımlı ayarıyla okuduğunda uzunluk **18**'e iniyor ve satır
başı sayısı **0** oluyor: çözücü her satır sonu biçimini tek bir karaktere çevirdi.
`newline=""` verildiğinde çeviri kapanıyor ve uzunluk **20**, satır başı sayısı **2** oluyor.

Satır sayısı üç okumada da **3**. Çeviri satırların **nerede bittiğini** değiştirmez, yalnız
satır sonunun kaç karakterle yazıldığını değiştirir. Metin kipinde okunan bir dosyanın
uzunluğunu dosyanın boyutuyla karşılaştırmak bu yüzden yanlış sonuç verir: hem kodlama hem
satır sonu çevirisi araya girer.

## Yazma Tarafı ve Açma Kipleri

Okuma tarafı kodlamayı **varsayar**; yazma tarafı onu **kurar**. Üçüncü ölçüm aynı metni beş
kodlamayla yazmayı dener ve her birinde gidiş–dönüş sınaması yapar: kodlanan baytlar aynı
kodlamayla geri çözüldüğünde kaynak metin geri gelir mi?

- **HD28** — Gidiş–dönüş sınaması her kodlamada **kendi** kodlamasıyla yapılır; ölçülen şey
  kodlamanın metni taşıyıp taşıyamadığıdır.
- **HD29** — Açma kipleri aynı dosya üzerinde **sırayla** denenir; her adımda dosyanın bayt
  sayısı dosya sisteminden okunur, hesaplanmaz.
- **HD30** — Dosya yine geçici bir dizinde kurulur; hiçbir yol basılmaz ve ölçüm bitince dizin
  silinir.

```python
"""Yazma tarafi: kodlanamayan karakter ve dosya acma kipleri."""
import tempfile
from pathlib import Path

METIN = "kuzey yamaç: 12,5 °C — ölçüm tamam"

print(f"{'kodlama':<12s}{'kodlandı':<10s}{'bayt':>6s}"
      f"{'gidiş-dönüş özdeş':>19s}   kırılan sınıf / konum")
for ad in ("utf-8", "utf-16", "latin-1", "ascii", "cp1254"):
    try:
        ham = METIN.encode(ad)
    except UnicodeEncodeError as e:
        print(f"  {ad:<10s}{'hayır':<10s}{'-':>6s}{'-':>19s}"
              f"   {type(e).__name__} / {e.start}")
    else:
        ozdes = "evet" if ham.decode(ad) == METIN else "hayır"
        print(f"  {ad:<10s}{'evet':<10s}{len(ham):>6d}{ozdes:>19s}   -")

print()
with tempfile.TemporaryDirectory() as gecici:
    gunluk = Path(gecici) / "olcum.log"
    print(f"{'kip':<6s}{'çağrı':<8s}{'dosya baytı':>12s}   sonuç")
    for sira, kip in (("1.", "w"), ("2.", "w"), ("3.", "a"), ("4.", "x")):
        try:
            with open(gunluk, kip, encoding="utf-8") as d:
                d.write(METIN + "\n")
        except FileExistsError as e:
            print(f"  {kip:<4s}{sira:<8s}{gunluk.stat().st_size:>12d}"
                  f"   {type(e).__name__}")
        else:
            print(f"  {kip:<4s}{sira:<8s}{gunluk.stat().st_size:>12d}   yazıldı")
    with open(gunluk, "r", encoding="utf-8") as d:
        satirlar = d.readlines()
    print(f"son durumda satır {len(satirlar)}, "
          f"karakter {sum(len(s) for s in satirlar)}, "
          f"bayt {gunluk.stat().st_size}")
```

```
kodlama     kodlandı    bayt  gidiş-dönüş özdeş   kırılan sınıf / konum
  utf-8     evet          41               evet   -
  utf-16    evet          70               evet   -
  latin-1   hayır          -                  -   UnicodeEncodeError / 21
  ascii     hayır          -                  -   UnicodeEncodeError / 10
  cp1254    evet          34               evet   -

kip   çağrı    dosya baytı   sonuç
  w   1.                42   yazıldı
  w   2.                42   yazıldı
  a   3.                84   yazıldı
  x   4.                84   FileExistsError
son durumda satır 2, karakter 70, bayt 84
```

`cp1254` satırı okuma tablosunun karşıtıdır ve dersin en öğretici çiftidir. Orada `cp1254` utf-8
baytlarını **sessizce yanlış** çözüyordu; burada aynı kodlama metni **34** bayta yazıyor ve
gidiş–dönüş **özdeş** çıkıyor. Kodlama bir dosyanın özelliği değildir; **yazanla okuyan
arasındaki anlaşmadır** ve dosyanın kendisi o anlaşmayı taşımaz. Yanlış olan `cp1254` değildi,
utf-8 ile yazılmış baytları `cp1254` sanmaktı.

Yazma tarafı okuma tarafından bir noktada ayrılır: `latin-1` ve `ascii` burada **kırılıyor**.
`latin-1` **21**'inci karakterde, uzun çizgide; `ascii` **10**'uncu karakterde, ilk Türkçe
harfte. Yazarken kodlanamayan karakter sessizce geçemez, çünkü karakterin karşılığı
kodlamada **yoktur**. Okurken sessiz yanlış mümkündü, çünkü her baytın bir karşılığı vardı.
`utf-16` metni **70** bayta yazıyor — utf-8'in **41**'ine karşı — ve o da özdeş dönüyor;
kodlama seçimi doğruluk kadar **boyut** kararıdır.

Açma kipleri tablosu üç ayrı davranışı ayırıyor. `"w"` ilk çağrıda **42** bayt yazıyor; ikinci
`"w"` çağrısından sonra dosya yine **42** — kip dosyayı **budayıp** baştan yazdı, ikinci kayıt
birinciyi sildi. `"a"` çağrısı **84**'e çıkarıyor: ekleme kipi budamaz. `"x"` çağrısı hiç
yazmadan `FileExistsError` fırlatıyor ve bayt sayısı **84**'te kalıyor. Son satır dosyanın
**2** satır, **70** karakter ve **84** bayt tuttuğunu söylüyor; karakter ile bayt farkı burada
da duruyor.

## Özet

- İkili kip `bytes` verir ve uzunluk bayt sayısıdır; metin kipi `str` verir, uzunluk karakter
  sayısıdır — aynı dosya **41** ve **34** okunur.
- Yanlış kodlama her zaman kırılmaz: `ascii` **10**'uncu baytta, `utf-16` **40**'ıncı baytta
  kırılırken `latin-1` ve `cp1254` hiç kırılmaz ve sessizce **41** yanlış karakter üretir.
- Hata kipleri kırılmayı engeller, veriyi kurtarmaz: `ignore` **28**, `replace` **41**,
  `backslashreplace` **80** karakter verir ve hiçbiri kaynakla özdeş değildir.
- Baytları parça parça çözmek parça sınırına bağlıdır: aynı **41** bayt boya göre **0**, **2**
  ya da **4** parçada kırılır, bütün olarak çözüldüğünde **0**.
- Kodlama hatası `open` çağrısında değil `read` çağrısında çıkar; satır sonu çevirisi uzunluğu
  **21**'den **18**'e indirir ama satır sayısını **3** olarak korur.
- Kodlama dosyanın değil, yazanla okuyanın özelliğidir: `cp1254` yazarken **34** baytla özdeş
  gidiş–dönüş verirken utf-8 baytlarını okurken sessizce bozar; yazarken kodlanamayan karakter
  `latin-1`'de **21**'inci, `ascii`'de **10**'uncu karakterde kırılır.

## Sonraki Adım

Bu dersin bütün dosya örnekleri `with` bloğu içinde açıldı ve blok bitince dosya kapandı —
ölçüm sonunda geçici dizin bile silinebildi, çünkü açık kalan bir tutamak kalmamıştı. `with`
şimdiye kadar bir alışkanlık olarak kullanıldı; oysa o da bir sözdizimdir ve altında bir
protokol vardır. Sonraki ders o protokolü ölçer: `with` girerken hangi özel yöntemi çağırır,
çıkarken hangisini, ve blok içinde **bir istisna oluştuğunda** ne değişir. Ortak tanımın
`with n + istisna` satırının üç çağrısı orada ödenir.
