Kiste
EN

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.txt und figuren/held.txt ins selbe Archiv packst, hätten beide den Eintragsnamen held.txt — das wäre nicht eindeutig. pack wirft dann, statt still eine Datei zu überschreiben. Willst du die Ordnerstruktur behalten, nimm zip_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.txt enthalten und damit Dateien außerhalb deines Zielordners überschreiben (das nennt man „Zip-Slip"). pack.zip_entpacke erkennt 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
19 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 ist stufe 0 deshalb 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 09.

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)