The book is currently only available in German.
Standard-Bibliothek — `pack`
Manchmal willst du viele Dateien als eine weitergeben — mehrere Bilder verschicken, einen Spielstand sichern, ein Projekt archivieren. Genau dafür gibt es das .zip: ein Umschlag, der beliebig viele Dateien enthält und sie dabei kleiner macht.
pack gibt Kiste zwei Werkzeuge:
- ZIP — mehrere Dateien in ein Archiv bündeln (und wieder auspacken).
- gzip — einen einzelnen Datenblock schrumpfen.
Wie db steckt darunter nur Kistes eingebaute Maschinerie — du musst nichts installieren, und es läuft in kiste run genau wie in der fertigen .exe.
37.1 Mehrere Dateien in ein ZIP — zip_erstelle
Du gibst ein Ziel-Archiv und eine Liste von Dateipfaden an:
nutze pack
nutze datei
datei.schreibe("held.txt", "Ich bin der Held")
datei.schreibe("gegner.txt", "Ich bin der Gegner")
pack.zip_erstelle("werke.zip", ["held.txt", "gegner.txt"])
sag "Archiv gebaut"
Ausgabe:
Archiv gebaut
Im Archiv heißen die Einträge nur held.txt und gegner.txt — der Dateiname, ohne Ordnerpfad. Das ist genau, was man erwartet, wenn man ein paar Dateien verschickt: aufmachen, Dateien da.
Zwei Dateien mit gleichem Namen? Wenn du
bilder/held.txtundfiguren/held.txtins selbe Archiv packst, hätten beide den Eintragsnamenheld.txt— das wäre nicht eindeutig.packwirft dann, statt still eine Datei zu überschreiben. Willst du die Ordnerstruktur behalten, nimmzip_aus_ordner(§37.2).
37.2 Einen ganzen Ordner packen — zip_aus_ordner
Statt einer Dateiliste gibst du einen Ordner an — pack nimmt alles darin, auch Unterordner:
nutze pack
nutze datei
// Ein kleiner Ordner zum Zeigen
datei.erstelle_ordner_rekursiv("bibliothek/tiere")
datei.schreibe("bibliothek/liesmich.txt", "Meine Sammlung")
datei.schreibe("bibliothek/tiere/katze.txt", "Miau")
pack.zip_aus_ordner("galerie.zip", "bibliothek")
sag "Ordner gepackt"
Ausgabe:
Ordner gepackt
Anders als bei zip_erstelle bleibt hier die Struktur erhalten: die Datei bibliothek/tiere/katze.txt liegt im Archiv unter tiere/katze.txt.
37.3 Reinschauen ohne Auspacken — zip_einträge
Du willst wissen, was in einem Archiv steckt, ohne es auszupacken:
nutze pack
nimm einträge = pack.zip_einträge("werke.zip")
sag "Einträge: {länge(einträge)}"
wiederhole e in einträge {
nimm n = e["name"]
nimm g = e["größe"]
sag " {n} — {g} Bytes"
}
Ausgabe:
Einträge: 2
held.txt — 16 Bytes
gegner.txt — 18 Bytes
zip_einträge liefert eine liste<karte> — eine Karte pro Eintrag, mit "name", "größe" (die echte, unkomprimierte Größe) und "komprimiert" (wie viel im Archiv belegt ist).
37.4 Wieder auspacken — zip_entpacke
nutze pack
nutze datei
nimm anzahl = pack.zip_entpacke("werke.zip", "ausgabe")
sag "{anzahl} Dateien entpackt"
sag datei.inhalt("ausgabe/held.txt")
Ausgabe:
2 Dateien entpackt
Ich bin der Held
Den Zielordner (ausgabe) legt pack selbst an, samt aller Unterordner. Zurück kommt die Zahl der entpackten Dateien.
Sicherheit — Archive sind nicht immer nett. Ein bösartiges Archiv könnte einen Eintrag wie
../../wichtig.txtenthalten und damit Dateien außerhalb deines Zielordners überschreiben (das nennt man „Zip-Slip").pack.zip_entpackeerkennt das und wirft — ein solches Archiv wird gar nicht erst ausgepackt. Um bestehende Dateien im Zielordner brauchst du dir keine Sorgen zu machen, die werden einfach überschrieben.
37.5 Ohne Datei: im Speicher packen
Manchmal willst du ein Archiv bauen, ohne die einzelnen Teile vorher auf die Platte zu schreiben — zum Beispiel, um es direkt zu verschicken. Dafür gibt es zip_von_bytes (packt eine karte<text, Bytes>) und zip_zu_bytes (packt wieder aus).
nutze pack
nutze bytes
nutze wert
// Name → Inhalt, alles im Speicher
nimm inhalt = {"gruss.txt": bytes.von_text("Hallo"), "zahl.txt": bytes.von_text("42")}
nimm archiv = pack.zip_von_bytes(inhalt) // ein Bytes-Wert — das ganze .zip
// ... und wieder auspacken
nimm dateien = pack.zip_zu_bytes(archiv)
nimm roh = wert.als_bytes(dateien["gruss.txt"])
sag bytes.als_text(roh)
Ausgabe:
Hallo
Die Werte in der Karte von zip_zu_bytes sind beliebig-verpackte Bytes — mit wert.als_bytes(...) holst du den echten Bytes-Wert heraus (wie bei db, das dir auch beliebig liefert). Die Reihenfolge der Einträge im Archiv ist die Reihenfolge, in der du sie in die Karte gelegt hast.
37.6 Einen einzelnen Block schrumpfen — gzip
gzip ist für einen Block gedacht — einen Bytes-Wert oder eine Datei. Kein Umschlag mit mehreren Einträgen, sondern schlicht „mach das hier kleiner":
nutze pack
nutze bytes
nimm roh = bytes.von_text("viel viel Text zum Schrumpfen")
nimm klein = pack.gzip(roh)
nimm zurück = pack.entgzip(klein)
sag bytes.als_text(zurück)
Ausgabe:
viel viel Text zum Schrumpfen
Für Dateien gibt es die bequemen Kurzformen pack.gzip_datei(quelle, ziel) und pack.entgzip_datei(quelle, ziel) — sie lesen, komprimieren und schreiben in einem Schritt.
37.7 Wie stark komprimieren? — stufe
Alle packenden Funktionen nehmen als letztes, optionales Argument eine stufe (ganz):
stufe |
Wirkung |
|---|---|
| weggelassen | Standard — gute Wahl für Text, JSON, CSV |
0 |
gar nicht komprimieren (nur speichern) — schnell |
1 … 9 |
schwach … maximal |
nutze pack
nutze datei
datei.schreibe("bericht.csv", "Name,Punkte\nAnna,42\nBob,37\n")
pack.zip_erstelle("stark.zip", ["bericht.csv"], 9) // maximal quetschen
pack.zip_erstelle("schnell.zip", ["bericht.csv"], 0) // nur speichern
sag "beide gebaut"
Ausgabe:
beide gebaut
Warum bei Bildern
stufe 0? PNG und JPEG sind schon komprimiert. Nochmal draufzusetzen bringt fast nichts und kostet nur Rechenzeit. Für ein Archiv voller Bilder iststufe 0deshalb die schlauere Wahl: gleich groß, aber schneller. Bei Text, JSON oder CSV lohnt sich die Kompression dagegen richtig.
37.8 Fehler abfangen
Wie bei db wirft jede pack-Funktion bei einem Fehler — du fängst ihn mit versuche/fange:
nutze pack
versuche {
pack.zip_entpacke("gibtsnicht.zip", "raus")
sag "unerreichbar"
} fange e {
sag "Konnte nicht entpacken"
}
Ausgabe:
Konnte nicht entpacken
Typische Fälle: Quelle fehlt, kein gültiges Archiv, ein Zip-Slip-Eintrag, zwei gleiche Dateinamen, oder eine stufe außerhalb 0…9.
37.9 Alle pack-Funktionen
| Funktion | Liefert |
|---|---|
pack.zip_erstelle(ziel, dateien[, stufe]) |
— |
pack.zip_aus_ordner(ziel, ordner[, stufe]) |
— |
pack.zip_von_bytes(inhalt[, stufe]) |
Bytes |
pack.zip_zu_bytes(daten) |
karte<text, Bytes> |
pack.zip_entpacke(quelle, zielordner) |
entpackte Dateien (ganz) |
pack.zip_einträge(quelle) |
liste<karte> |
pack.gzip(daten[, stufe]) |
Bytes |
pack.entgzip(daten) |
Bytes |
pack.gzip_datei(quelle, ziel[, stufe]) |
— |
pack.entgzip_datei(quelle, ziel) |
— |