Python'da Dosya İşlemleri
Bu makale, Python'daki dosya işlemlerini açıklar.
Bu rehber, "güvenlik", "verimlilik" ve "okunabilirlik" dikkate alınarak, kavramları temelden pratik tekniklere adım adım açıklar.
YouTube Video
Python'da Dosya İşlemleri
Dosya işlemleri, küçük scriptlerden büyük ölçekli uygulamalara kadar temel ve vazgeçilmez bir beceridir.
Dosyaları açma ve kapama
Öncelikle, metin dosyalarını okuma ve yazma örneklerine bakalım. with ifadesi (bağlam yöneticisi) kullanmak, dosyanın düzgün şekilde kapanmasını sağlar.
Aşağıdaki kod, bir metin dosyasını açar, içeriğini okur ve satır satır işler.
1# Open and read a text file safely using a context manager.
2with open("example.txt", "r", encoding="utf-8") as f:
3 # Read all lines lazily (as an iterator) and process each line.
4 for line in f:
5 print(line.rstrip())encoding="utf-8"değerini açıkça belirtmek, platforma bağlı sorunları azaltmaya yardımcı olur.- Büyük dosyalarda bile,
for line in fbellek açısından verimlidir.
Yazma (üzerine yazma ve ekleme)
Dosyaya yazarken kullanılan moda dikkat edin. w, dosyanın üzerine yazmak, a ise dosyaya ekleme yapmak içindir. Yazma işlemlerinde de with ifadesini kullanın.
Aşağıdaki kod, üzerine yazma ve ekleme işlemlerinin temel örneklerini gösterir.
1# Write (overwrite) to a file.
2with open("output.txt", "w", encoding="utf-8") as f:
3 f.write("First line\n")
4 f.write("Second line\n")
5
6# Append to the same file.
7with open("output.txt", "a", encoding="utf-8") as f:
8 f.write("Appended line\n")- Bu kod, önce
output.txtdosyasına yazı yazar, ardından aynı dosyaya ekleme yapar."w"modu dosyanın üzerine yazar;"a"modu ise mevcut içeriğin sonuna yeni bir satır ekler. - Eğer zorunluysa
flush()fonksiyonunu çağırın; ancak genellikle içerik, bağlam sona erdiğinde otomatik olarak temizlenir (flush edilir). - Birden fazla işlemin veya iş parçacığının aynı anda yazma ihtimali varsa, dosya kilitleme gibi münhasır kontrolü düşünmelisiniz.
İkili (binary) veri okuma ve yazma
Resim ve sıkıştırılmış dosyalar, ikili modda (rb veya wb) işlenir. Metin modunun aksine, encoding parametresi dikkate alınmaz.
Aşağıdaki kod, bir ikili dosyayı okur ve başka bir dosyaya ikili modda kopyalar.
1# Read and write binary files.
2with open("image.png", "rb") as src:
3 data = src.read()
4
5with open("copy.png", "wb") as dst:
6 dst.write(data)- Büyük ikili dosyaları işlerken, tamamını bir defada
read()ile yüklemekten kaçının; parça parça (chunk) okuma ve yazma daha verimlidir.
Büyük dosyaların parça parça işlenmesi örneği
Belleğe sığmayan çok büyük dosyalar için, onları sabit boyutlu parçalara bölerek okuyun ve yazın. İşlem G/Ç (I/O) tabanlı olduğundan, tampon (buffer) boyutunu ayarlamak faydalı olacaktır.
Aşağıdaki kod, bir dosyayı 64KB'lık parçalar halinde kopyalar. Bu yöntem, belleği az kullanarak hızlı çalışır.
1# Copy a large file in chunks to avoid using too much memory.
2CHUNK_SIZE = 64 * 1024 # 64 KB
3
4with open("large_source.bin", "rb") as src, open("large_dest.bin", "wb") as dst:
5 while True:
6 chunk = src.read(CHUNK_SIZE)
7 if not chunk:
8 break
9 dst.write(chunk)- Parça boyutunu, ağınızın veya diskinizin özelliklerine göre ayarlayabilirsiniz. SSD'lerde, biraz daha büyük bir parça boyutu genellikle daha iyi sonuç verir.
Buffering argümanının kullanımına ilişkin örnek
open() fonksiyonunda buffering argümanını belirterek, Python'un dahili olarak kullandığı tampon (buffer) boyutunu kontrol edebilirsiniz. Bu, giriş/çıkış verimliliğini daha fazla optimize etmenizi sağlar.
1# Explicitly set the internal buffer size to 128 KB for faster I/O.
2CHUNK_SIZE = 64 * 1024
3BUFFER_SIZE = 128 * 1024 # 128 KB internal buffer
4
5with open("large_source.bin", "rb", buffering=BUFFER_SIZE) as src, open("large_dest.bin", "wb", buffering=BUFFER_SIZE) as dst:
6 while True:
7 chunk = src.read(CHUNK_SIZE)
8 if not chunk:
9 break
10 dst.write(chunk)bufferingdeğerini 0 olarak ayarlarsanız, G/Ç işlemleri tamponlama olmadan gerçekleştirilir; 1 satır tamponlamasını etkinleştirir, 2 veya daha yüksek değerler ise belirli bayt boyutunda bir tampon kullanır.- Genel olarak işletim sistemi önbellekleme işlemini verimli bir şekilde yönettiği için varsayılan değer yeterlidir, ancak bu parametrenin ayarlanması çok büyük dosyalar veya özel aygıtlar için etkili olabilir.
Modern dosya işlemleri için Pathlib kullanımı
Standart pathlib modülü, yol (path) işlemlerini daha sezgisel hale getirir. Dize (string) yollarla karşılaştırıldığında, okunabilirliği ve güvenliği artırır.
1from pathlib import Path
2
3path = Path("data") / "notes.txt"
4
5# Ensure parent directory exists.
6path.parent.mkdir(parents=True, exist_ok=True)
7
8# Write and read using pathlib.
9path.write_text("Example content\n", encoding="utf-8")
10content = path.read_text(encoding="utf-8")
11print(content)- Bu kod,
pathlibile dizin oluşturmayı, bir metin dosyasına yazmayı ve içeriğini okumayı gösterir.Pathnesnesi sayesinde, yolları hem sezgisel hem de güvenli bir şekilde yönetebilirsiniz. Pathnesnesinin,iterdir()veglob()gibi kullanışlı API'leri vardır. Farklı işletim sistemlerinde yol ayırıcıları konusunda endişelenmeden kod yazabilirsiniz.
Geçici dosya ve dizinler (tempfile)
Geçici dosyalar, tempfile ile güvenli bir şekilde oluşturulabilir. Bu, güvenlik yarış durumlarını ve isim çakışmalarını önler.
Aşağıdaki kod, geçici bir dosya kullanarak geçici veri oluşturma örneğini gösterir.
1import tempfile
2
3# Create a temporary file that is deleted on close.
4with tempfile.NamedTemporaryFile(
5 mode="w+",
6 encoding="utf-8",
7 delete=True
8) as tmp:
9 tmp.write("temporary data\n")
10 tmp.seek(0)
11 print(tmp.read())
12
13# tmp is deleted here- Bu kod, geçici bir dosya oluşturur, veri yazar ve okur ve
withbloğu sona erdiğinde dosyayı otomatik olarak siler.tempfile.NamedTemporaryFilekullanarak, geçici dosyaları güvenli ve çakışmasız bir şekilde yönetebilirsiniz.delete=Truebelirtildiği için dosya otomatik olarak silinir. - Windows'ta, dosyayı başka bir tutamaçtan (handle) hemen açamayabilirsiniz; bu yüzden
delete=Falseolarak ayarlayabilir ve silmeyi kendiniz yönetebilirsiniz.
shutil: Kopyalama, taşıma ve silme için yüksek seviyeli işlemler
shutil ile dosya ve dizinlerin özyinelemeli (recursive) olarak kopyalanması, taşınması ve silinmesi kolaydır.
1import shutil
2
3# Copy a file preserving metadata.
4shutil.copy2("source.txt", "dest.txt")
5
6# Move a file or directory.
7shutil.move("old_location", "new_location")
8
9# Remove an entire directory tree (use with caution).
10shutil.rmtree("some_directory")shutil.copy2, değişiklik zamanı gibi meta verileri de kopyalar.move, yeniden adlandırma (rename) kullanılamasa bile dosyayı taşımayı dener.rmtreetehlikeli bir işlemdir; silmeden önce mutlaka onaylayın ve yedeğinizi alın.
Dosya meta verisi (os.stat) ve yetkilendirme işlemleri
Dosya boyutu, değiştirilme zamanı ve yetkiler, os ve stat ile okunabilir ve değiştirilebilir.
Aşağıdaki kod, temel dosya bilgilerini os.stat ile elde eder ve yetkileri os.chmod ile değiştirir.
1import os
2import stat
3from datetime import datetime
4
5st = os.stat("example.txt")
6print("size:", st.st_size)
7print("modified:", datetime.fromtimestamp(st.st_mtime))
8
9# Make file read-only for owner.
10os.chmod("example.txt", stat.S_IREAD)- Yetkilendirme işlemleri, POSIX ve Windows sistemlerinde farklılık gösterir. Platformlar arası uyumluluk önemliyse, yüksek seviyeli API'leri kullanın veya koşullu işleme ekleyin.
Dosya kilitleme (münhasır kontrol) — Unix ve Windows arasındaki farklar
Birden fazla sürecin aynı dosyaya aynı anda erişmesi durumunda münhasır kontrol gereklidir. UNIX sistemleri fcntl kullanır, Windows ise msvcrt kullanır.
Aşağıdaki kod, UNIX sistemlerinde yazarken münhasır kilit almak için fcntl.flock kullanır.
1# Unix-style file locking example
2import fcntl
3
4with open("output.txt", "a+", encoding="utf-8") as f:
5 fcntl.flock(f, fcntl.LOCK_EX)
6 try:
7 f.write("Safe write\n")
8 f.flush()
9 finally:
10 fcntl.flock(f, fcntl.LOCK_UN)- Bu kod, UNIX benzeri sistemlerde güvenli yazmak ve eşzamanlı yazıları önlemek için
fcntl.flockile münhasır bir kilit alır. İşlem bittikten sonra kilidi mutlaka bırakın ki diğer işlemler erişebilsin. - Windows'ta
msvcrt.locking()fonksiyonunu kullanın. Daha üst düzey kullanım için,portalockerveyafilelockgibi harici kütüphaneleri düşünebilirsiniz.
Atomik dosya yazma desenleri
Güncellemeler sırasında dosya bozulmasını önlemek için, önce geçici bir dosyaya yazın ve başarılı olunca os.replace ile değiştirin.
Önce geçici dosyaya yazmak ve sonra değiştirmek, bir çökme olursa bozulmayı önler.
1import os
2from pathlib import Path
3import tempfile
4
5def atomic_write(path: Path, data: str, encoding="utf-8"):
6 # Write to a temp file in the same directory and atomically replace.
7 dirpath = path.parent
8 with tempfile.NamedTemporaryFile(
9 mode="w",
10 encoding=encoding,
11 dir=str(dirpath),
12 delete=False
13 ) as tmp:
14 tmp.write(data)
15 tmp.flush()
16 tempname = tmp.name
17
18 # Atomic replacement (on most OSes)
19 os.replace(tempname, str(path))
20
21# Usage
22atomic_write(Path("config.json"), '{"key":"value"}\n')os.replace, aynı dosya sistemi içinde atomik bir şekilde değiştirme işlemi yapar. Farklı bağlı noktalar (mount) arasında atomiklik garanti edilmez, buna dikkat edin.
Büyük ölçekli veri için mmap ile hızlı erişim
Büyük dosyalara rastgele erişim için, mmap G/Ç performansını artırır. Ağırlıklı olarak ikili (binary) işlemler içerir.
Aşağıdaki kod, bir dosyayı bellek eşlem (memory-map) yapar ve belirli bayt aralıklarını okur veya yazar. Dosya boyutunu değiştirirken dikkatli olun.
1import mmap
2
3with open("data.bin", "r+b") as f:
4 mm = mmap.mmap(f.fileno(), 0) # map entire file
5 try:
6 print(mm[:20]) # first 20 bytes
7 mm[0:4] = b"\x00\x01\x02\x03" # modify bytes
8 finally:
9 mm.close()- Bu kod, bir ikili dosyayı
mmapile belleğe eşler ve doğrudan bayt düzeyinde okuma/yazma işlemleri yapar. Belleğe eşleme (memory mapping), büyük veri setlerine hızlı ve rastgele erişim sağlar. mmapverimlidir, ancak yanlış kullanılırsa veri tutarlılığı sorunlarına yol açabilir. Gerektiğinde senkronize etmek içinflush()fonksiyonunu çağırın.
CSV / JSON / Pickle: Formata göre okuma ve yazma
Belirli veri formatları için özel modüller vardır. CSV için csv, JSON için json, Python nesnelerini kaydetmek için pickle kullanın.
Aşağıdaki kod, CSV ile JSON okuma-yazma ve Pickle kullanımı için temel örnekler sunar. Pickle, rastgele kod çalıştırabildiğinden, güvenilmeyen kaynaklardan veri yüklemekten kaçının.
1import csv
2import json
3import pickle
4
5# CSV write
6with open("rows.csv", "w", encoding="utf-8", newline="") as f:
7 writer = csv.writer(f)
8 writer.writerow(["name", "age"])
9 writer.writerow(["Alice", 30])
10
11# JSON write
12data = {"items": [1, 2, 3]}
13with open("data.json", "w", encoding="utf-8") as f:
14 json.dump(data, f, ensure_ascii=False, indent=2)
15
16# Pickle write (only for trusted environments)
17obj = {"key": "value"}
18with open("obj.pkl", "wb") as f:
19 pickle.dump(obj, f)- Windows'ta fazladan boş satır oluşmasını önlemek için, CSV'de
newline=""parametresi önerilir.ensure_ascii=Falseile JSON çıktısı, UTF-8 karakterlerini okunabilir şekilde tutar.
Sıkıştırılmış dosyaların (gzip / bz2 / zipfile) doğrudan okunması ve yazılması
Gzip ve zip dosyalarını doğrudan işlemek, disk alanından tasarruf sağlar. Standart kütüphanede ilgili modüller bulunmaktadır.
Aşağıdaki kod, gzip dosyalarını metin olarak okuma ve yazma için basit bir örnektir.
1import gzip
2
3# Write gzipped text.
4with gzip.open("text.gz", "wt", encoding="utf-8") as f:
5 f.write("Compressed content\n")
6
7# Read gzipped text.
8with gzip.open("text.gz", "rt", encoding="utf-8") as f:
9 print(f.read())- Sıkıştırma oranı ile hız arasında, sıkıştırma seviyesi ve formata bağlı olarak bir denge vardır.
Güvenlik ve zafiyet önlemleri
Güvenlik ve zafiyet önlemleri için aşağıdaki noktalar göz önünde bulundurulabilir.
- Dosya adlarında veya yollarında güvenilmeyen veriyi doğrudan kullanmayın.
- Pickle'ı yalnızca güvenilir kaynaklarla kullanın.
- Dosyaları yöneten işlemlere yalnızca asgari izinleri verin, çalıştırma ayrıcalıklarını en aza indirin.
tempfileile geçici dosyalar kullanın ve düz dosyaları kamuya açık dizinlerde tutmayın.
Kullanıcı girdisini dosya yolunda kullanıyorsanız, normalizasyon ve geçerlilik denetimi zorunludur. Örneğin, Path.resolve() kullanın ve üst dizinleri denetleyin.
1from pathlib import Path
2
3def safe_path(base_dir: Path, user_path: str) -> Path:
4 candidate = (base_dir / user_path).resolve()
5 if base_dir.resolve() not in candidate.parents and base_dir.resolve() != candidate:
6 raise ValueError("Invalid path")
7 return candidate
8
9# Usage
10base = Path("/srv/app/data")
11
12# Raises an exception: attempted path traversal outside `base`
13safe = safe_path(base, '../etc/passwd')- Web uygulamalarında veya genel API'lerde, dış girdileri dosya yolu olarak kullanırken özellikle dikkatli olun.
Yaygın desenlerin özeti
- Daima
withifadesini kullanın (otomatik kapama). - Metin verisi için
encodingparametresini açıkça belirleyin. - Büyük dosyaları parça parça okuyun ve yazın.
- Paylaşılan kaynaklar için dosya kilitleme uygulayın.
- Kritik güncellemeler için 'geçici bir dosyaya yaz → os.replace' şeklinde atomik bir yöntem kullanın.
- Tehlikeli işlemler (silme veya üzerine yazma gibi) yapmadan önce daima onay alın ve yedek oluşturun.
- Harici giriş dosya yolu olarak kullanılırken normalleştirme ve doğrulama yapın.
Özet
Dosya işlemlerinde, with ifadesini kullanmak, kodlamayı açıkça belirtmek ve atomik yazmalar gibi güvenli ve güvenilir teknikler kullanmak önemlidir. Büyük ölçekli işlemler veya paralel erişimlerde, veri bozulması ve çakışmaları önlemek için kilitleme ve günlük yönetim sistemleri uygulanmalıdır. Verimlilik ve güvenlik arasında denge kurmak, güvenilir dosya işlemlerinin anahtarıdır.
Yukarıdaki makaleyi, YouTube kanalımızda Visual Studio Code'u kullanarak takip edebilirsiniz. Lütfen YouTube kanalını da kontrol edin.