Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Antwoorden — Hoofdstuk 8: Bestanden lezen en schrijven

Opmerking: Code is in het Engels, met korte Nederlandse toelichting erboven of eronder.

Antwoord 1 — Merge .txt bestanden

from pathlib import Path


def merge_txt(dir_path: str | Path, out_name: str = "all.txt") -> None:
    d = Path(dir_path)
    out = d / out_name
    parts: list[str] = []
    for p in sorted(d.glob("*.txt")):
        if p.name == out_name:
            continue  # skip the output file itself if present
        text = p.read_text(encoding="utf-8", errors="replace")
        parts.append(text.rstrip("\n"))
    out.write_text("\n\n".join(parts) + ("\n" if parts else ""), encoding="utf-8")


if __name__ == "__main__":
    merge_txt("")

Antwoord 2 — tail(path, n)

from pathlib import Path
from collections import deque

def tail(path: str | Path, n: int) -> list[str]:
    # Simple approach: read all lines and slice. For medium files this is ok.
    lines = Path(path).read_text(encoding="utf-8", errors="replace").splitlines()
    return lines[-n:] if n > 0 else []

if __name__ == "__main__":
    print(tail("poem.txt", 5))

Antwoord 3 — copy_file (binair)

from pathlib import Path

def copy_file(src: str | Path, dst: str | Path, chunk_size: int = 8192) -> None:
    src_p = Path(src)
    dst_p = Path(dst)
    with src_p.open("rb") as f_in, dst_p.open("wb") as f_out:
        while True:
            chunk = f_in.read(chunk_size)
            if not chunk:
                break
            f_out.write(chunk)

if __name__ == "__main__":
    copy_file("image.jpg", "copy.jpg")

Antwoord 4 — grep‑achtig filter (case‑insensitive)

from pathlib import Path

def grep_ci(path: str | Path, needle: str) -> None:
    n = needle.lower()
    with Path(path).open("r", encoding="utf-8", errors="replace") as f:
        for line in f:
            if n in line.lower():
                print(line.rstrip())

if __name__ == "__main__":
    grep_ci("app.log", "error")

Antwoord 5 — CSV lezen (naïef) en net printen

from pathlib import Path

def print_items_table(csv_path: str | Path) -> None:
    rows: list[tuple[str, int, float]] = []
    with Path(csv_path).open("r", encoding="utf-8", newline="") as f:
        for line in f:
            name, qty, price = line.rstrip("\n").split(",")
            rows.append((name.strip(), int(qty), float(price)))
    total = 0.0
    print(f"{'Item':<12}{'Qty':>5}{'Price':>10}{'Total':>10}")
    for name, qty, price in rows:
        line_total = qty * price
        total += line_total
        print(f"{name:<12}{qty:>5}{price:>10.2f}{line_total:>10.2f}")
    print(f"{'Grand total:':<27}{total:>10.2f}")

if __name__ == "__main__":
    print_items_table("items.csv")

Antwoord 6 — JSON schrijven en lezen

from pathlib import Path
import json

def write_config(path: Path, data: dict) -> None:
    path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")

def read_config(path: Path) -> dict:
    text = path.read_text(encoding="utf-8")
    return json.loads(text)

if __name__ == "__main__":
    cfg_path = Path("config.json")
    data = {"name": "Ava", "age": 30}
    try:
        write_config(cfg_path, data)
        loaded = read_config(cfg_path)
        print(loaded.get("name"))
    except FileNotFoundError:
        print("Config file not found")
    except json.JSONDecodeError as e:
        print("Invalid JSON:", e)

Antwoord 7 — Atomisch schrijven

from pathlib import Path
import os

def atomic_write_text(path: Path, text: str) -> None:
    tmp = path.with_suffix(path.suffix + ".tmp")
    tmp.write_text(text, encoding="utf-8")
    os.replace(tmp, path)

if __name__ == "__main__":
    atomic_write_text(Path("data.txt"), "new content\n")

Antwoord 8 — Newline normalisatie

from pathlib import Path

def write_unix_newlines(path: Path, lines: list[str]) -> None:
    with path.open("w", encoding="utf-8", newline="\n") as f:
        f.writelines([line.rstrip("\n") + "\n" for line in lines])

if __name__ == "__main__":
    write_unix_newlines(Path("out.txt"), ["A", "B", "C"]) 

Antwoord 9 — Safe read_text

from pathlib import Path
from typing import Optional

def safe_read_text(path: Path) -> Optional[str]:
    try:
        return path.read_text(encoding="utf-8")
    except FileNotFoundError:
        print("File not found:", path)
        return None
    except PermissionError:
        print("No permission to read:", path)
        return None

if __name__ == "__main__":
    print(safe_read_text(Path("maybe.txt")))

Antwoord 10 — Directory listing met groottes

from pathlib import Path

def list_dir(path: str | Path) -> None:
    p = Path(path)
    files = 0
    total_bytes = 0
    for entry in p.iterdir():
        if entry.is_dir():
            kind = "DIR"
            size_str = ""
        else:
            kind = "FILE"
            size = entry.stat().st_size
            total_bytes += size
            files += 1
            size_str = f" {size:>8d} B"
        print(f"{kind:4} {entry.name}{size_str}")
    print(f"-- Files: {files}, Total bytes: {total_bytes}")

if __name__ == "__main__":
    list_dir(".")

Uitdaging (optioneel) — Mini To‑Do CLI met bestand

from pathlib import Path
import sys

TODO = Path("todo.txt")


def add_item(text: str) -> None:
    with TODO.open("a", encoding="utf-8") as f:
        f.write(text.strip() + "\n")


def list_items() -> None:
    if not TODO.exists():
        print("No items yet")
        return
    with TODO.open("r", encoding="utf-8") as f:
        for i, line in enumerate(f, start=1):
            print(f"{i}. {line.rstrip()}")


def done(n: int) -> None:
    # choice: remove the line permanently
    if not TODO.exists():
        return
    lines = TODO.read_text(encoding="utf-8").splitlines()
    if 1 <= n <= len(lines):
        del lines[n - 1]
        TODO.write_text("\n".join(lines) + ("\n" if lines else ""), encoding="utf-8")


if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: todo.py [add <text>|list|done <n>]")
        raise SystemExit(1)
    cmd = sys.argv[1]
    if cmd == "add" and len(sys.argv) >= 3:
        add_item(" ".join(sys.argv[2:]))
    elif cmd == "list":
        list_items()
    elif cmd == "done" and len(sys.argv) >= 3:
        try:
            n = int(sys.argv[2])
        except ValueError:
            print("Please provide an integer index for 'done'.")
            raise SystemExit(1)
        done(n)
    else:
        print("Unknown command or missing arguments.")
        raise SystemExit(1)