Python'ın yerleşik dosya işleme özelliği, dilin gerçek güçlü yanlarından biridir — temel okuma/yazma işlemleri için herhangi bir içe aktarma gerekmez ve API bir öğleden sonraya öğrenebilecek kadar temizdir. Ancak tutorial sürümü ile gerçekte göndereceğiniz şey arasında gerçek bir boşluk vardır. Tutorial sürümü bir dosya açar, okur ve kapatır. Üretim sürümü, verileri sessizce bozan kodlama uyumsuzluklarıyla, macOS'ta çalışan ama Windows'ta çöken yollarla ve 2 GB'lık bir dosyada read() çağırırsanız tüm belleğinizi sessizce yiyen log dosyalarıyla başa çıkar. Bu makale dayanıklı kalıpları kapsar — sadece mutlu yolu değil.

with İfadesi — Her Zaman Kullanın

Python'daki her dosya işleme örneği bir bağlam yöneticisi kullanmalıdır — okuma sırasında bir istisna oluşsa bile dosyanın kapatılmasını sağlayan with bloğu. Bir bağlam yöneticisi, bir with bloğuna giriş ve çıkışta ne olacağını tanımlayan bir nesnedir; dosyalar için, çıkış close()'un otomatik olarak çağrılması anlamına gelir. Pratikte neden önemli olduğu:

python
# ❌ Manual close — works until it doesn't
f = open('app.log')
data = f.read()   # if this raises an exception...
f.close()         # ...this line never runs. File handle leaks.

# ✅ Context manager — close() is guaranteed
with open('app.log') as f:
    data = f.read()
# file is closed here, no matter what happened inside the block

Uzun süreli çalışan sunucularda bu akademik değildir — dosya tanıtıcılarını sızdırmak sonunda OSError: [Errno 24] Too many open files hatasına neden olur. with ifadesi hiçbir şeye mal olmaz ve bu hata sınıfını tamamen önler. Her yerde kullanın.

Dosya Okuma — Dört Yol, Her Seferinde Bir Doğru Araç

Python size bir dosya nesnesi üzerinde çeşitli yöntemler sunar ve doğru olanı seçmek, çoğu tutorialın kabul ettiğinden daha fazla önem taşır:

  • f.read() — tüm dosyayı tek bir dizeye okur. Küçük yapılandırma dosyaları için uygundur, büyükler için tehlikelidir.
  • f.readline() — iç işaretçiyi ilerletirken bir satırı bir seferde okur. İterasyon üzerinde manuel kontrol gerektiğinde kullanışlıdır.
  • f.readlines() — tüm satırları bir listeye okur. Kullanışlıdır, ancak yine de tüm dosyayı belleğe yükler.
  • for line in f: — yineleyici protokolü. Tam dosyayı yüklemeden bir seferde bir satır okur. Varsayılan olarak bu tercih edilmeli.

İşte gerçekçi bir örnek: .env tarzı bir yapılandırma dosyasını okuyup bir sözlüğe dönüştürme. Bu gerçekten yazdığınız türden bir şeydir, uydurma bir "hello.txt oku" demosu değil:

python
from pathlib import Path

def load_config(path: str) -> dict:
    """Read a key=value config file, ignoring comments and blank lines."""
    config = {}
    with open(path, encoding='utf-8') as f:
        for line in f:
            line = line.strip()
            if not line or line.startswith('#'):
                continue
            if '=' not in line:
                continue
            key, _, value = line.partition('=')
            config[key.strip()] = value.strip()
    return config

# Usage
settings = load_config('config/app.conf')
db_host = settings.get('DB_HOST', 'localhost')
.strip() alışkanlığı: Satırları okurken, son satır hariç her satır sondaki bir \n içerir (ve Windows'ta, \r\n). Her ikisini de kaldırmak için line.strip() çağırın. Yalnızca yeni satırı kaldırmak ve baştaki boşluğu değil istiyorsanız, bunun yerine line.rstrip('\n') kullanın.

Yazma ve Ekleme — Hangi Modun Verileri Yok Ettiğini Bilin

open()'ın ikinci argümanı moddur. İki mod insanları tekrar tekrar zorluyor:

  • 'w' — yazma modu. Dosyayı yazmak için açar. Dosya zaten varsa, tek bir karakter yazmadan önce hemen sıfır bayta kısaltılır. Yanlış yolu açarsanız bu sessiz veri yok etmesidir.
  • 'a' — ekleme modu. Dosyayı açar ve yazma işaretçisini sona taşır. Mevcut içerik asla dokunulmaz. Yeni yazmalar zaten olanın arkasına gider.

Ekleme modu için iyi bir kullanım senaryosu, zaman damgalı yapılandırılmış bir log dosyası yazmaktır. İşte hem betiklerde hem de küçük servislerde kullanışlı olan bir kalıp:

python
import datetime

LOG_FILE = 'logs/pipeline.log'

def log_event(level: str, message: str) -> None:
    timestamp = datetime.datetime.utcnow().isoformat() + 'Z'
    line = f"[{timestamp}] {level.upper()}: {message}\n"
    with open(LOG_FILE, 'a', encoding='utf-8') as f:
        f.write(line)

log_event('info', 'Pipeline started')
log_event('warning', 'Retrying connection to database')
log_event('error', 'Failed to parse row 4821 — skipping')
Uyarı: open(path, 'w') dosyayı yoksa oluşturur — bu kullanışlıdır — ama varsa sessizce yok eder. Yanlış yazılan bir yol, herhangi bir hata mesajı olmadan bir üretim dosyasını silebilir. Dosyanın üzerine yazılıp yazılmaması gerektiğinden emin değilseniz, önce Path(path).exists() ile kontrol edin veya üzerine yazmak yerine FileExistsError fırlatan 'x' modunu kullanın.

Kodlama — Herkesi Eninde Sonunda Isıran Hata

Bu, Python dosya işlemede sessiz veri bozulmasının en yaygın kaynağıdır. Python 3'ün bir tane belirtmeden open() çağırdığınızda varsayılan kodlaması, locale.getpreferredencoding() tarafından belirlenir — Windows'ta bu genellikle cp1252'dir ve Linux/macOS'ta genellikle UTF-8'dir. Bu, Mac'inizde mükemmel çalışan kodun, dosya ASCII dışında herhangi bir karakter içerdiğinde bir Windows sunucusunda sessizce bozulabileceği veya çökebileceği anlamına gelir. Düzeltme tek bir ek argümandır:

python
# ❌ Platform-dependent — works on Linux, corrupts on Windows
with open('customers.csv') as f:
    data = f.read()

# ✅ Explicit UTF-8 — same behavior on every platform
with open('customers.csv', encoding='utf-8') as f:
    data = f.read()

# For files exported from Excel on Windows — may have a BOM (byte order mark)
# utf-8-sig strips the BOM automatically on read
with open('export.csv', encoding='utf-8-sig') as f:
    data = f.read()

BOM sorunu, Microsoft Excel'den dışa aktarılan CSV dosyalarında özellikle yaygındır — dosya yanlış kodlamayla okunursa  olarak görünen gizli bir \ufeff karakteriyle başlar veya ilk sütun başlığının name yerine name gibi görünmesine neden olur. encoding='utf-8-sig' kullanmak bunu şeffaf bir şekilde ele alır. Kodlama adlarının tam listesi için Python codecs belgelerine bakın.

Genel kural: Her open() çağrısına her zaman encoding='utf-8' (veya Excel dışa aktarmaları için 'utf-8-sig') geçirin. Alışkanlık haline getirin — hiçbir maliyeti yoktur ve ortama özgü tüm hata kategorisini ortadan kaldırır.

Yollarla Çalışma — pathlib Kullanın

Python'da dosya yolları oluşturmanın eski yolu, dize birleştirme veya os.path.join()'di. Modern yol, pathlib.Path'tir, Python 3.4'ten beri kullanılabilir ve 3.6'dan itibaren tamamen olgunlaşmıştır. Düşünmenize gerek kalmadan Windows ve Unix'te yol ayırıcılarını doğru şekilde yönetir ve bir avuç os.path çağrısını okunabilir öznitelik erişimiyle değiştirir.

python
from pathlib import Path

# Build a path relative to the current script — works on Windows and Unix
base_dir = Path(__file__).parent
data_dir = base_dir / 'data'
input_file = data_dir / 'records.csv'

# Check existence before opening
if not input_file.exists():
    raise FileNotFoundError(f"Input file not found: {input_file}")

# Create a directory (including parents) without error if it already exists
output_dir = base_dir / 'output' / 'reports'
output_dir.mkdir(parents=True, exist_ok=True)

# Iterate over all JSON files in a directory
for json_file in data_dir.glob('*.json'):
    print(json_file.name)       # just the filename: 'records.json'
    print(json_file.stem)       # filename without extension: 'records'
    print(json_file.suffix)     # extension: '.json'
    print(json_file.parent)     # parent directory as a Path

# The / operator builds paths — no os.path.join needed
report_path = output_dir / f"report_{input_file.stem}.txt"

/ operatörü burada bölme değildir — Path bunu yol birleştirme anlamında geçersiz kılar. Bu doğal okunur ve dizeye dayalı yol oluşturmayla gelen alıntılama ve ayırıcı sorunlarını ortadan kaldırır. Bir kullanışlı yöntem daha: path.read_text(encoding='utf-8'), dosyanın içeriğini yalnızca dize olarak istediğinizde açma/okuma/kapatma kalıbı için bir kısayoldur.

Büyük Dosyaları Belleği Patlatmadan Okuma

Bir dosya küçük olduğunda — diyelim ki birkaç megabaytın altında — f.read() veya f.readlines() uygundur. 500 MB'lık bir sunucu günlüğü veya çok gigabaytlık bir veri dışa aktarımı olduğunda, tüm şeyi belleğe yüklemek bir MemoryError'a veya işletim sisteminden bir işlem sonlandırmasına giden hızlı bir yoldur. Düzeltme satır satır iterasyondur:

python
from pathlib import Path
from collections import Counter

def count_error_levels(log_path: str) -> dict:
    """
    Process a large log file line by line.
    Memory usage stays roughly constant regardless of file size.
    """
    counts = Counter()
    with open(log_path, encoding='utf-8') as f:
        for line in f:
            # Each line is fetched from disk as needed — not loaded all at once
            if ' ERROR ' in line:
                counts['error'] += 1
            elif ' WARN ' in line:
                counts['warning'] += 1
            elif ' INFO ' in line:
                counts['info'] += 1
    return dict(counts)

results = count_error_levels('/var/log/app/server.log')
print(f"Errors: {results.get('error', 0)}, Warnings: {results.get('warning', 0)}")

for line in f: kalıbı çalışır çünkü Python'ın dosya nesnesi yineleyici protokolünü uygular — dahili bir arabellek kullanarak satırları diskten birer birer getirir, bu nedenle bellek kullanımı dosya boyutundan bağımsız olarak özünde sabit kalır. Satır satır iterasyonun bile yeterince hızlı olmadığı gerçekten büyük dosyalar için (onlarca gigabayt), mmap dosyayı okumadan belleğe eşlemenizi ve düzenli ifadelerle aramanızı sağlar — ancak çoğu kullanım senaryosu için satır yineleyicisi ihtiyacınız olan tek şeydir.

JSON ve CSV Okuma ve Yazma

Gerçek Python çalışmasında iki format sürekli ortaya çıkar ve her ikisinin de alıntılama, kaçış ve yapıyı doğru şekilde işleyen özel stdlib modülleri vardır — bunları dize bölmeleriyle ayrıştırmayın.

python
import json
import csv
from pathlib import Path

# --- JSON ---
# Reading
with open('config/settings.json', encoding='utf-8') as f:
    settings = json.load(f)            # parsed directly from the file object

# Writing (indent=2 gives readable output)
with open('output/results.json', 'w', encoding='utf-8') as f:
    json.dump(results, f, indent=2, ensure_ascii=False)

# --- CSV ---
# Reading
with open('data/customers.csv', encoding='utf-8-sig', newline='') as f:
    reader = csv.DictReader(f)         # each row is a dict keyed by header
    for row in reader:
        process_customer(row['email'], row['plan'])

# Writing
fieldnames = ['id', 'email', 'plan', 'created_at']
with open('output/export.csv', 'w', encoding='utf-8', newline='') as f:
    writer = csv.DictWriter(f, fieldnames=fieldnames)
    writer.writeheader()
    for record in records:
        writer.writerow(record)

Dikkat edilmesi gereken birkaç şey: CSV dosyalarını açarken newline='' geçirin — csv modülü kendi satır sonlarını yönetir ve Python'ın evrensel yeni satır modunun müdahale etmesine izin vermek, Windows'ta yinelenen boş satırlara neden olur. JSON için, ensure_ascii=False, ASCII olmayan karakterlerin (aksanlı harfler, CJK karakterleri, vb.) \uXXXX dizilerine kaçırılmak yerine olduğu gibi yazılmasını sağlar — çok daha okunabilir çıktı. JSON veya CSV verileriyle çalışıyorsanız ve bunları görsel olarak incelemek veya dönüştürmek istiyorsanız, bu sitedeki JSON Biçimlendirici ve CSV Biçimlendirici, kod yaklaşımına iyi tamamlayıcılardır.

Hata İşleme — Göreceğiniz Üç İstisna

Dosya işlemleri öngörülebilir şekillerde başarısız olur. Her durumu açıkça işlemek, genel bir traceback yerine gerçekten kullanışlı hata mesajları sağlar:

python
import json
from pathlib import Path

def load_json_config(path: str) -> dict:
    """
    Load a JSON config file with explicit error handling.
    Returns the parsed config or raises with a clear message.
    """
    config_path = Path(path)

    try:
        with open(config_path, encoding='utf-8') as f:
            return json.load(f)

    except FileNotFoundError:
        raise FileNotFoundError(
            f"Config file not found: {config_path.resolve()}\n"
            f"Create it or set CONFIG_PATH to the correct location."
        )

    except PermissionError:
        raise PermissionError(
            f"No read permission on {config_path.resolve()}\n"
            f"Check file ownership and mode (chmod 644 on Linux)."
        )

    except UnicodeDecodeError as e:
        raise ValueError(
            f"Encoding error reading {config_path}: {e}\n"
            f"Try opening with encoding='utf-8-sig' if the file came from Windows."
        )

    except json.JSONDecodeError as e:
        raise ValueError(
            f"Invalid JSON in {config_path} at line {e.lineno}, col {e.colno}: {e.msg}"
        )

Dört istisna neredeyse her gerçek başarısızlık modunu kapsar: dosya mevcut değil, izniniz yok, kodlama yanlış veya içerik hatalı biçimlendirilmiş. Her mesaj bir sonraki geliştiriciye (veya saat 02:00'de size) tam olarak neyin yanlış gittiğini ve nereye bakacağını söyler. Çıplak bir Exception yakalamak ve "bir şeyler ters gitti" yazdırmak kullanışlı hata işleme değildir — sadece karışıklığı aşağı taşır.

Ne zaman yeniden fırlatmak, ne zaman None döndürmek: Eksik bir yapılandırma dosyası betiğiniz için ölümcül bir hataysa, arayanın yüksek sesle başarısız olması için yeniden fırlatın (veya yeni bir istisna fırlatın). Opsiyonelse — diyelim ki kullanıcı başına geçersiz kılma dosyası — FileNotFoundError'ı yakalamak ve None veya varsayılan bir sözlük döndürmek uygundur. Fonksiyon başına bir davranış seçin ve belgeleyin.

Sonuç

Yukarıdakilerin kısa versiyonu: her zaman with bloklarını kullanın, her zaman encoding='utf-8' geçirin, yol oluşturma için pathlib.Path kullanın ve boyut bilinmediğinde tüm dosyaları okumak yerine satırlar üzerinde yineleme yapın. Bu dört alışkanlık, dosya işleme hatalarının büyük çoğunluğunu üretime ulaşmadan önce ortadan kaldırır.

Daha derin okuma için: Dosya okuma ve yazma hakkındaki Python tutorial bölümü temelleri kapsamlı şekilde ele alır. pathlib belgeleri yer imi eklemeye değer — stdlib'in en kullanışlı parçalarından biridir ve Python geliştiricilerinin çoğu bunu yeterince kullanmaz. csv modülü belgeleri ve json modülü belgeleri her ikisi de bu formatlarla düzenli olarak çalışıyorsanız okumaya değer uç durumlar için iyi örneklere (özel sınırlayıcılar, akışlı JSON, vb.) sahiptir.