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)