Dateioperationen in Python
Dieser Artikel erklärt Dateioperationen in Python.
Mit Blick auf „Sicherheit“, „Effizienz“ und „Lesbarkeit“ erklärt dieser Leitfaden die Konzepte Schritt für Schritt, von den Grundlagen bis zu praktischen Techniken.
YouTube Video
Dateioperationen in Python
Dateioperationen sind eine grundlegende und unverzichtbare Fähigkeit, von kleinen Skripten bis zu groß angelegten Anwendungen.
Dateien öffnen und schließen
Sehen wir uns zunächst Beispiele für das Lesen und Schreiben von Textdateien an. Die Verwendung der with-Anweisung (Kontextmanager) stellt sicher, dass die Datei ordnungsgemäß geschlossen wird.
Der folgende Code öffnet eine Textdatei, liest deren Inhalt und verarbeitet ihn Zeile für Zeile.
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())- Die explizite Angabe von
encoding="utf-8"hilft, plattformspezifische Probleme zu minimieren. - Selbst bei großen Dateien ist
for line in fspeichereffizient.
Schreiben (Überschreiben und Anhängen)
Achten Sie beim Schreiben in Dateien auf den Modus. w dient zum Überschreiben, a zum Anhängen. Verwenden Sie auch beim Schreiben die with-Anweisung.
Der folgende Code zeigt grundlegende Beispiele zum Überschreiben und Anhängen.
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")- Dieser Code schreibt Text in
output.txtund fügt dann weiteren Text zu derselben Datei hinzu. Der Modus"w"überschreibt die Datei, während der Modus"a"eine neue Zeile an das Ende des bestehenden Inhalts anhängt. - Falls Sie ein Flush benötigen, rufen Sie
flush()auf, aber normalerweise wird der Inhalt automatisch beim Verlassen des Kontexts geschrieben. - Wenn mehrere Prozesse oder Threads gleichzeitig schreiben können, müssen Sie exklusive Steuerung wie Dateisperren berücksichtigen.
Binärdaten lesen und schreiben
Bilder und komprimierte Dateien werden im Binärmodus (rb oder wb) verarbeitet. Im Gegensatz zum Textmodus wird encoding ignoriert.
Der folgende Code liest eine Binärdatei und kopiert sie im Binärmodus in eine andere Datei.
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)- Beim Umgang mit großen Binärdateien sollten Sie vermeiden, alles mit
read()auf einmal zu lesen; das Lesen und Schreiben in Blöcken ist speichereffizienter.
Beispiel für das Bearbeiten großer Dateien in Blöcken
Für sehr große Dateien, die nicht in den Speicher passen, lesen und schreiben Sie sie in Blöcken mit fester Größe. Da es sich um I/O-lastige Vorgänge handelt, ist die Anpassung der Puffergröße sinnvoll.
Der folgende Code kopiert eine Datei in 64-KB-Blöcken. Dies arbeitet schnell bei gleichzeitig geringem Speicherverbrauch.
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)- Sie können die Blockgröße entsprechend den Eigenschaften Ihres Netzwerks oder Ihrer Festplatte anpassen. Bei SSDs funktioniert oft eine etwas größere Blockgröße besser.
Beispiel für die Verwendung des Arguments 'buffering'
Indem Sie das Argument buffering in der Funktion open() angeben, können Sie die von Python intern verwendete Puffergröße steuern. Dies ermöglicht es Ihnen, die Ein- und Ausgabe-Effizienz weiter zu optimieren.
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)- Wenn Sie den Wert von
bufferingauf 0 setzen, erfolgt die Ein-/Ausgabe ohne Pufferung; 1 aktiviert Zeilenpufferung, während Werte von 2 oder höher einen Puffer der angegebenen Größe in Bytes verwenden. - Im Allgemeinen ist der Standardwert ausreichend, da das Betriebssystem das Caching effizient verwaltet, aber die Anpassung dieses Parameters kann bei sehr großen Dateien oder speziellen Geräten sinnvoll sein.
Moderne Dateioperationen mit Pathlib
Das Standardmodul pathlib macht die Pfadbehandlung intuitiver. Es verbessert die Lesbarkeit und Sicherheit im Vergleich zu Zeichenkettenpfaden.
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)- Dieser Code zeigt das Erstellen eines Verzeichnisses mit
pathlib, das Schreiben in eine Textdatei und das Lesen ihres Inhalts. Mit einemPath-Objekt können Sie Pfade sowohl intuitiv als auch sicher handhaben. Pathbietet praktische APIs wieiterdir()undglob(). Sie können Code schreiben, ohne sich um Pfadtrenner zwischen verschiedenen Betriebssystemen zu kümmern.
Temporäre Dateien und Verzeichnisse (tempfile)
Temporäre Dateien können sicher mit tempfile erstellt werden. Dadurch werden Sicherheits-Race-Conditions und Namenskonflikte vermieden.
Der folgende Code zeigt ein Beispiel für das Erstellen temporärer Daten mit einer temporären Datei.
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- Dieser Code erstellt eine temporäre Datei, schreibt und liest Daten und löscht sie automatisch, wenn der
with-Block endet. Mittempfile.NamedTemporaryFilekönnen temporäre Dateien sicher und ohne Konflikte verwendet werden. Dadelete=Trueangegeben ist, wird die Datei automatisch gelöscht. - Unter Windows kann es sein, dass die Datei nicht sofort von einem anderen Handle geöffnet werden kann. In diesem Fall können Sie
delete=Falsesetzen und die Löschung selbst verwalten.
shutil: Hochrangige Operationen zum Kopieren, Verschieben und Löschen
Rekursives Kopieren, Verschieben und Löschen von Dateien und Verzeichnissen ist mit shutil einfach.
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.copy2kopiert auch Metadaten (wie Änderungszeit).moveweicht auf das Verschieben der Datei aus, selbst wennrenamenicht verwendet werden kann.rmtreeist eine gefährliche Operation, daher sollten Sie vor dem Löschen immer bestätigen und eine Sicherung erstellen.
Datei-Metadaten (os.stat) und Rechteverwaltung
Dateigröße, Änderungszeitpunkt und Berechtigungen können mit os und stat gelesen und geändert werden.
Der folgende Code ruft mit os.stat grundlegende Dateiinformationen ab und ändert die Berechtigungen mit os.chmod.
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)- Berechtigungen verhalten sich auf POSIX- und Windows-Systemen unterschiedlich. Wenn plattformübergreifende Kompatibilität wichtig ist, verwenden Sie hochrangige APIs oder fügen Sie bedingte Behandlungen hinzu.
Dateisperren (exklusive Kontrolle) — Unterschiede zwischen Unix und Windows
Exklusive Kontrolle ist erforderlich, wenn mehrere Prozesse gleichzeitig auf dieselbe Datei zugreifen. UNIX verwendet fcntl, während Windows msvcrt verwendet.
Der folgende Code verwendet fcntl.flock auf UNIX-Systemen, um beim Schreiben eine exklusive Sperre zu erhalten.
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)- Dieser Code erlangt mit
fcntl.flockauf Unix-ähnlichen Systemen eine exklusive Sperre, um sicher zu schreiben und gleichzeitige Schreibzugriffe auf die Datei zu verhindern. Geben Sie die Sperre nach der Verarbeitung immer frei, um den Zugriff für andere Prozesse zu ermöglichen. - Unter Windows verwenden Sie
msvcrt.locking(). Für eine komfortablere Handhabung sollten Sie externe Bibliotheken wieportalockeroderfilelockin Betracht ziehen.
Atomare Schreibmuster für Dateien
Um Dateibeschädigungen bei Aktualisierungen zu vermeiden, schreiben Sie in eine temporäre Datei und ersetzen sie bei Erfolg mit os.replace.
Das Schreiben in eine temporäre Datei und anschließende Ersetzen verhindert eine Beschädigung, falls ein Absturz auftritt.
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 führt einen atomaren Austausch innerhalb desselben Dateisystems durch. Beachten Sie, dass Atomizität über verschiedene Mountpunkte hinweg nicht garantiert ist.
Schneller Zugriff mit mmap (für große Datenmengen)
Für den wahlfreien Zugriff auf große Dateien verbessert mmap die I/O-Leistung. Es handelt sich dabei hauptsächlich um Binäroperationen.
Der folgende Code mapped eine Datei in den Speicher und liest oder schreibt bestimmte Bytebereiche. Seien Sie vorsichtig beim Ändern der Dateigröße.
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()- Dieser Code mappt eine Binärdatei mit
mmapin den Speicher und führt direkte Lese/Schreib-Operationen auf Byte-Ebene durch. Memory Mapping ermöglicht einen schnellen wahlfreien Zugriff auf große Datensätze. mmapist effizient, aber unsachgemäße Verwendung kann zu Dateninkonsistenzen führen. Rufen Sie bei Bedarfflush()auf, um zu synchronisieren.
CSV / JSON / Pickle: Lesen und Schreiben in spezifischen Formaten
Für bestimmte Datenformate gibt es spezielle Module. Verwenden Sie csv für CSV, json für JSON und pickle zum Speichern von Python-Objekten.
Der folgende Code zeigt einfache Beispiele für das Lesen und Schreiben von CSV und JSON sowie für die Verwendung von Pickle. Da Pickle beliebigen Code ausführen kann, sollten Sie das Laden von Daten aus nicht vertrauenswürdigen Quellen vermeiden.
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)- Für CSV empfiehlt es sich,
newline=""anzugeben, um zusätzliche Leerzeilen unter Windows zu vermeiden. Mitensure_ascii=Falsebleiben UTF-8-Zeichen im JSON lesbar.
Direktes Lesen und Schreiben komprimierter Dateien (gzip / bz2 / zipfile)
Das direkte Verarbeiten von gzip und zip kann Speicherplatz sparen. Die Standardbibliothek enthält entsprechende Module.
Der folgende Code ist ein einfaches Beispiel für das Lesen und Schreiben von gzip-Dateien als Text.
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())- Je nach Kompressionsstufe und Format besteht ein Kompromiss zwischen Kompressionsrate und Geschwindigkeit.
Sicherheits- und Schwachstellenmaßnahmen
Die folgenden Punkte können für Sicherheits- und Schwachstellenmaßnahmen berücksichtigt werden.
- Verwenden Sie keine nicht vertrauenswürdigen Eingaben direkt in Dateinamen oder Pfaden.
- Verwenden Sie Pickle nur mit vertrauenswürdigen Quellen.
- Minimieren Sie die Ausführungsrechte und geben Sie den Prozessen, die mit Dateien arbeiten, nur die unbedingt nötigen Berechtigungen.
- Verwenden Sie temporäre Dateien mit
tempfileund speichern Sie keine unverschlüsselten Dateien in öffentlichen Verzeichnissen.
Wenn Benutzereingaben in Dateipfaden verwendet werden, sind Normalisierung und Validierung erforderlich. Verwenden Sie beispielsweise Path.resolve() und prüfen Sie die übergeordneten Verzeichnisse.
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')- Seien Sie besonders vorsichtig, wenn Sie externe Eingaben als Dateipfade in Webanwendungen oder öffentlichen APIs verwenden.
Zusammenfassung häufiger Muster
- Verwenden Sie immer die
with-Anweisung (automatisches Schließen). - Geben Sie für Textdaten das
encodingexplizit an. - Lesen und schreiben Sie große Dateien in Blöcken.
- Führen Sie Dateisperren für gemeinsam genutzte Ressourcen ein.
- Für kritische Aktualisierungen verwenden Sie das atomare Muster 'Schreiben in eine temporäre Datei → os.replace'.
- Bestätigen Sie immer und erstellen Sie Sicherungskopien, bevor Sie gefährliche Operationen (wie Löschen oder Überschreiben) durchführen.
- Normalisieren und validieren Sie bei der Verwendung externer Eingaben als Dateipfade.
Zusammenfassung
Für Dateioperationen ist es wichtig, sichere und zuverlässige Techniken zu verwenden, wie die Verwendung der with-Anweisung, die explizite Angabe der Codierung und atomare Schreibvorgänge. Für großflächige Verarbeitungen oder parallelen Zugriff ist es notwendig, Sperr- und Protokollmanagementsysteme zu implementieren, um Datenkorruption und Konflikte zu vermeiden. Das Gleichgewicht zwischen Effizienz und Sicherheit ist der Schlüssel zu zuverlässigen Dateioperationen.
Sie können den obigen Artikel mit Visual Studio Code auf unserem YouTube-Kanal verfolgen. Bitte schauen Sie sich auch den YouTube-Kanal an.