Bestandbewerkingen in Python
Dit artikel legt bestandbewerkingen in Python uit.
Met 'veiligheid', 'efficiëntie' en 'leesbaarheid' in gedachten legt deze gids stap voor stap de concepten uit, van de basis tot praktische technieken.
YouTube Video
Bestandbewerkingen in Python
Bestandsbewerkingen zijn een essentiële basisvaardigheid, van kleine scripts tot grootschalige applicaties.
Bestanden openen en sluiten
Laten we eerst voorbeelden bekijken van het lezen en schrijven van tekstbestanden. Het gebruik van de with-instructie (contextmanager) zorgt ervoor dat het bestand correct wordt gesloten.
De volgende code opent een tekstbestand, leest de inhoud en verwerkt deze regel voor regel.
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())- Door expliciet
encoding="utf-8"op te geven, worden platformafhankelijke problemen verminderd. - Zelfs bij grote bestanden is
for line in fgeheugen-efficiënt.
Schrijven (overschrijven en toevoegen)
Let goed op de modus bij het schrijven naar bestanden. w is om te overschrijven, a is om toe te voegen. Gebruik de with-instructie ook bij het schrijven.
De volgende code laat basisvoorbeelden zien van overschrijven en toevoegen.
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")- Deze code schrijft tekst naar
output.txten voegt daarna meer tekst toe aan hetzelfde bestand. De"w"-modus overschrijft het bestand, terwijl de"a"-modus een nieuwe regel aan het einde van de bestaande inhoud toevoegt. - Als je moet flushen, roep dan
flush()aan, maar meestal wordt de inhoud automatisch geflusht als de context eindigt. - Als meerdere processen of threads gelijktijdig kunnen schrijven, moet u exclusieve controle overwegen, zoals bestandsvergrendeling.
Binaire gegevens lezen en schrijven
Afbeeldingen en gecomprimeerde bestanden worden verwerkt in binaire modus (rb of wb). In tegenstelling tot tekstmodus wordt encoding genegeerd.
De volgende code leest een binair bestand en kopieert het naar een ander bestand in binaire modus.
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)- Vermijd bij grote binaire bestanden het in één keer lezen met
read(); lezen en schrijven in stukken is geheugen-efficiënter.
Voorbeeld van grote bestanden in stukken verwerken
Lees en schrijf enorme bestanden die niet in het geheugen passen in stukken met een vaste grootte. Aangezien het I/O-afhankelijk is, is het handig om de buffergrootte aan te passen.
De volgende code kopieert een bestand in blokken van 64KB. Dit werkt snel terwijl het geheugengebruik laag blijft.
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)- Je kunt de blokgrootte aanpassen aan de kenmerken van je netwerk of schijf. Op SSD's werkt een iets grotere blokgrootte vaak beter.
Voorbeeld van het gebruik van het buffering-argument
Door het argument buffering op te geven in de functie open(), kun je de buffergrootte die intern door Python wordt gebruikt, beheren. Hierdoor kun je de efficiëntie van input/output verder optimaliseren.
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)- Als je de waarde van
bufferingop 0 zet, wordt I/O uitgevoerd zonder buffering; 1 schakelt line-buffering in, terwijl waarden van 2 of hoger een buffer van de opgegeven grootte in bytes gebruiken. - Over het algemeen is de standaardwaarde voldoende omdat het besturingssysteem efficiënt omgaat met caching, maar het aanpassen van deze parameter kan effectief zijn voor zeer grote bestanden of speciale apparaten.
Moderne bestandsmanipulatie met Pathlib
De standaardmodule pathlib maakt padbeheer intuïtiever. Het verbetert de leesbaarheid en veiligheid vergeleken met stringpaden.
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)- Deze code laat zien hoe je een map maakt met
pathlib, naar een tekstbestand schrijft en de inhoud leest. Met eenPath-object kun je paden op een intuïtieve en veilige manier beheren. Pathheeft handige API's zoalsiterdir()englob(). Je kunt code schrijven zonder je zorgen te maken over padseparators tussen verschillende besturingssystemen.
Tijdelijke bestanden en mappen (tempfile)
Tijdelijke bestanden kunnen veilig worden aangemaakt met tempfile. Dit voorkomt beveiligingsproblemen door race-omstandigheden en naamconflicten.
De volgende code laat een voorbeeld zien van het aanmaken van tijdelijke gegevens met een tijdelijk bestand.
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- Deze code maakt een tijdelijk bestand aan, schrijft en leest gegevens en verwijdert het automatisch zodra het
with-blok eindigt. Mettempfile.NamedTemporaryFilekun je tijdelijke bestanden veilig en zonder conflicten beheren. Omdatdelete=Trueis opgegeven, wordt het bestand automatisch verwijderd. - Op Windows kun je het bestand mogelijk niet direct openen vanuit een andere handle, dus kun je
delete=Falseinstellen en de verwijdering zelf beheren.
shutil: Hoog-niveau bewerkingen voor kopiëren, verplaatsen en verwijderen
Recursief kopiëren, verplaatsen en verwijderen van bestanden en mappen is eenvoudig met shutil.
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.copy2kopieert ook metadata (zoals de wijzigingstijd).movezal proberen het bestand te verplaatsen, zelfs als een rename niet kan worden gebruikt.rmtreeis een gevaarlijke operatie; bevestig en maak altijd een back-up voordat je verwijdert.
Bestandsmetadata (os.stat) en rechtenbeheer
Bestandsgrootte, wijzigingstijd en rechten kunnen worden gelezen en aangepast met os en stat.
De volgende code haalt basisinformatie over bestanden op met os.stat en wijzigt rechten met 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)- Rechten werken anders op POSIX- en Windows-systemen. Als platformonafhankelijke compatibiliteit belangrijk is, gebruik dan high-level API's of voeg voorwaardelijke afhandeling toe.
Bestandsvergrendeling (exclusieve controle) — Verschillen tussen Unix en Windows
Exclusieve controle is nodig wanneer meerdere processen gelijktijdig toegang hebben tot hetzelfde bestand. UNIX gebruikt fcntl en Windows gebruikt msvcrt.
De volgende code gebruikt fcntl.flock onder UNIX-systemen om een exclusieve vergrendeling te verkrijgen bij het schrijven.
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)- Deze code verkrijgt een exclusieve vergrendeling met
fcntl.flockop UNIX-achtige systemen om veilig te schrijven en gelijktijdige schrijfacties te voorkomen. Geef de vergrendeling altijd vrij na verwerking, zodat andere processen toegang krijgen. - Gebruik op Windows
msvcrt.locking(). Overweeg voor gebruik op een hoger niveau externe bibliotheken zoalsportalockeroffilelock.
Atomaire schrijfpatronen voor bestanden
Om beschadiging van bestanden tijdens updates te voorkomen, schrijf eerst naar een tijdelijk bestand en vervang het bij succes met os.replace.
Schrijven naar een tijdelijk bestand en daarna vervangen voorkomt beschadiging bij een crash.
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 voert een atomaire vervanging uit binnen hetzelfde bestandssysteem. Let op: atomiciteit is niet gegarandeerd tussen verschillende aankoppelingen.
Snelle toegang met mmap (voor grootschalige data)
Voor willekeurige toegang tot grote bestanden verbetert mmap de I/O-prestaties. Dit betreft vooral binaire bewerkingen.
De volgende code gebruikt geheugenmapping van een bestand en leest of schrijft specifieke groepjes bytes. Wees voorzichtig bij het wijzigen van de bestandsgrootte.
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()- Deze code koppelt een binair bestand aan het geheugen met
mmapen voert directe lees/schrijfoperaties op byteniveau uit. Geheugenmapping maakt snelle willekeurige toegang tot grote datasets mogelijk. mmapis efficiënt, maar verkeerd gebruik kan leiden tot inconsistenties in de data. Roepflush()aan om indien nodig te synchroniseren.
CSV / JSON / Pickle: Lezen en schrijven per formaat
Specifieke dataformaten hebben aparte modules. Gebruik csv voor CSV, json voor JSON en pickle om Python-objecten op te slaan.
De volgende code geeft basisvoorbeelden van het lezen en schrijven van CSV en JSON, en het gebruik van Pickle. Omdat Pickle willekeurige code kan uitvoeren, laad geen data van onbetrouwbare bronnen.
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)- Het is aan te raden
newline=""voor CSV te specificeren om extra lege regels op Windows te voorkomen. Metensure_ascii=Falseblijven UTF-8-tekens leesbaar in JSON.
Direct lezen en schrijven van gecomprimeerde bestanden (gzip / bz2 / zipfile)
Direct werken met gzip en zip kan schijfruimte besparen. De standaardbibliotheek bevat de bijbehorende modules.
De volgende code is een eenvoudig voorbeeld van het lezen en schrijven van gzip-bestanden als tekst.
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())- Er is een afweging tussen compressieverhouding en snelheid, afhankelijk van het compressieniveau en het formaat.
Beveiligings- en kwetsbaarheidsmaatregelen
De volgende punten kunnen worden overwogen voor beveiligings- en kwetsbaarheidsmaatregelen.
- Gebruik geen onbetrouwbare invoer direct in bestandsnamen of paden.
- Gebruik Pickle alleen met vertrouwde bronnen.
- Beperk uitvoeringsrechten tot een minimum en geef alleen de noodzakelijke rechten aan processen die bestanden verwerken.
- Gebruik tijdelijke bestanden met
tempfileen sla geen gewone bestanden op in openbare mappen.
Bij het gebruik van gebruikersinvoer in bestandspaden is normalisatie en validatie vereist. Gebruik bijvoorbeeld Path.resolve() en controleer bovenliggende mappen.
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')- Wees extra voorzichtig bij het gebruik van externe invoer als bestandspad in webapps of publieke API's.
Overzicht van veelgebruikte patronen
- Gebruik altijd de
with-instructie (automatisch sluiten). - Specificeer altijd expliciet
encodingvoor tekstgegevens. - Lees en schrijf grote bestanden in stukken.
- Voer bestandsvergrendeling in voor gedeelde bronnen.
- Gebruik voor kritieke updates het atomische patroon van 'schrijven naar een tijdelijk bestand → os.replace'.
- Bevestig altijd en maak een back-up voordat je gevaarlijke handelingen uitvoert (zoals verwijderen of overschrijven).
- Normaliseer en valideer wanneer je externe invoer als bestandspad gebruikt.
Samenvatting
Voor bestandsbewerkingen is het belangrijk om veilige en betrouwbare methoden te gebruiken, zoals het gebruik van de with-statement, het expliciet specificeren van encodering en atomisch schrijven. Voor grootschalige verwerking of gelijktijdige toegang is het noodzakelijk om vergrendelings- en logbeheersystemen te implementeren om gegevensbeschadiging en conflicten te voorkomen. Het balanceren van efficiëntie en veiligheid is de sleutel tot betrouwbare bestandsbewerkingen.
Je kunt het bovenstaande artikel volgen met Visual Studio Code op ons YouTube-kanaal. Bekijk ook het YouTube-kanaal.