Werkboek — Hoofdstuk 8: Bestanden lezen en schrijven
Let op: alle programmacode schrijf je in het Engels (zoals in het boek). De uitleg en opdrachten zijn in het Nederlands.
Doel van dit hoofdstuk
- Veilig werken met
with open(...)en tekst‐encodering (encoding="utf-8"). - Leespatronen voor tekst:
read(), regels itereren,readline(),read(n). - Schrijfpatronen:
write,writelines, newlines ennewline‑parameter. - Paden met
pathlib.Path: paden samenstellen, directories maken, globben. - Binaire I/O (chunks), defensief werken en veelvoorkomende valkuilen.
Richtlijnen
- Gebruik
withvoor alle bestandsoperaties. - Specificeer bij tekstbestanden
encoding="utf-8"expliciet. - Gebruik
pathlib.Pathwaar zinvol i.p.v. alleen stringpaden. - Voeg zelf
\ntoe bijwrite;print(..., file=f)voegt standaard een newline toe (tenzijendis aangepast).
Oefening 1 — Merge .txt bestanden
Schrijf een script dat alle .txt‑bestanden in een gegeven directory samenvoegt tot all.txt. Zet tussen ieder bronbestand een lege regel. Gebruik pathlib.Path.glob("*.txt").
Oefening 2 — tail(path, n)
Implementeer een functie tail(path: str | Path, n: int) -> list[str] die de laatste n regels van een tekstbestand teruggeeft (zonder trailing newlines). Efficiënt genoeg voor middelgrote bestanden (je mag het hele bestand inlezen en de laatste n regels nemen).
Oefening 3 — copy_file (binair)
Schrijf copy_file(src: str | Path, dst: str | Path, chunk_size: int = 8192) -> None die binaire data kopieert in chunks. Gebruik rb/wb en een lus met read(chunk_size).
Oefening 4 — grep‑achtig filter (case‑insensitive)
Schrijf een functie grep_ci(path: str | Path, needle: str) -> None die regels print die needle (case‑insensitive) bevatten. Open met errors="replace" zodat corrupte encodering niet crasht.
Oefening 5 — CSV lezen (naïef) en net printen
Gegeven een CSV‑bestand items.csv met kolommen name,qty,price. Lees het naïef met split(',') (weet: dit is niet robuust voor echte CSV). Print dan een tabel met nette kolombreedtes en een grand total.
Oefening 6 — JSON schrijven en lezen Schrijf twee helpers:
write_config(path: Path, data: dict) -> Nonedie JSON schrijft metensure_ascii=False, indent=2.read_config(path: Path) -> dictdie leest en het dict teruggeeft. Schrijf een__main__‑blok dat een klein config‑dict schrijft en daarna weer leest en een veld print. VangFileNotFoundErrorenjson.JSONDecodeErrormet duidelijke meldingen.
Oefening 7 — Atomisch schrijven
Schrijf een functie atomic_write_text(path: Path, text: str) -> None die eerst naar path.with_suffix(path.suffix + ".tmp") schrijft en daarna os.replace gebruikt om atomisch te vervangen. Voeg encoding toe.
Oefening 8 — Newline normalisatie
Schrijf write_unix_newlines(path: Path, lines: list[str]) -> None die altijd Unix‑newlines (\n) schrijft, ongeacht platform. Gebruik newline='\n' bij open en writelines.
Oefening 9 — Safe read_text
Schrijf safe_read_text(path: Path) -> str | None die de inhoud leest (utf‑8) of None teruggeeft bij FileNotFoundError of PermissionError, met een vriendelijke printmelding.
Oefening 10 — Directory listing met groottes
Schrijf een script dat alle items in een directory print met hun type (file/dir) en voor files ook de grootte in bytes. Toon onderaan het totaal aantal files en het totale aantal bytes. Gebruik Path.iterdir().
Uitdaging (optioneel) — Mini To‑Do CLI met bestand
Maak een klein CLI‑programma dat een tekstbestand todo.txt gebruikt in de huidige directory.
add <text>voegt een regel toe.listtoont genummerde regels.done <n>markeert itemnals gedaan door het te verwijderen of te prefixen met[x](kies één en beschrijf in een comment je keuze). Gebruikwithenencoding="utf-8"; onthoud dat het bestand niet hoeft te bestaan bij de eerste run.