Gzip

Dekomprimiert als Base64 geschriebenes gzip, zlib oder rohes deflate – welches, sagen die Bytes – und komprimiert Text in jedes der drei, im Browser.

Eingabe
oder eine hier ablegen — sie wird lokal gelesen und nie hochgeladen
Ausgabe
[
  {
    "name": "Lena",
    "city": "Berlin"
  },
  {
    "name": "Paul",
    "city": "Hamburg"
  },
  {
    "name": "Mia",
    "city": "München"
  },
  {
    "name": "Felix",
    "city": "Köln"
  },
  {
    "name": "Emma",
    "city": "Frankfurt am Main"
  },
  {
    "name": "Jonas",
    "city": "Leipzig"
  },
  {
    "name": "Hannah",
    "city": "Wien"
  },
  {
    "name": "Leon",
    "city": "Zürich"
  }
]

Als gzip gelesen.

Unkomprimierte Bytes
418
Komprimierte Bytes
172
Änderung
-58,9 %
Zeichen in Base64
232

Eine Kompression in drei Wrappern, und das Wort deflate

gzip, zlib und rohes deflate sind eine einzige Kompression, DEFLATE, auf drei Arten verpackt. RFC 1951 definiert DEFLATE selbst: Die Bytes werden in Blöcke zerlegt, und jeder wird als Codes geschrieben, die für ein Byte stehen oder für eine Folge, die von weiter vorn kopiert wird. Der Wrapper von gzip, RFC 1952, setzt einen Header von mindestens zehn Bytes davor und acht Bytes dahinter, eine CRC-32-Prüfsumme der ursprünglichen Bytes und deren Länge; der von zlib, RFC 1950, setzt zwei Bytes davor und eine Adler-32-Prüfsumme dahinter; rohes deflate ist die Kompression ganz ohne Wrapper. Die komprimierten Daten darin können in allen dreien dieselben sein, also macht der Wrapper den ganzen Unterschied aus.

Beim Wort deflate verheddern sich die Namen. Für HTTP bedeutet Content-Encoding: deflate den Wrapper von zlib, und RFC 9110 merkt an, dass manche Server unter diesem Namen trotzdem rohes deflate senden. Der Kompressor des Browsers selbst folgt HTTP: Den Wrapper von zlib nennt er deflate, und rohes deflate nennt er deflate-raw. DeflateStream von .NET und gzdeflate von PHP schreiben rohes deflate, während gzcompress von PHP und zlib.compress von Python den Wrapper von zlib schreiben. Als deflate ausgewiesene Bytes können also beides sein, und welches, können nur die Bytes sagen.

Deshalb liest die Seite beim Dekomprimieren den Wrapper aus den Bytes und sagt unter dem Ergebnis, welchen sie gelesen hat: „Als gzip gelesen“, „Als zlib gelesen“ oder „Als rohes deflate gelesen“. Die Bytes entscheiden es: gzip beginnt immer mit 1f 8b, die ersten zwei Bytes von zlib ergeben, zusammen als eine einzige Zahl gelesen, ein Vielfaches von 31, und kein Encoder beginnt rohes deflate mit einem von beiden. Eine Datei mit der Endung .gz, die zlib enthält, wird als zlib gelesen, mit einem Hinweis, dass der Name und die Bytes nicht übereinstimmen, und Bytes, die in keinem der drei vorliegen, werden abgelehnt: Es gibt nichts zu dekomprimieren. Beim Komprimieren wählen Sie den Wrapper, und bis Sie einen anderen wählen, ist es gzip.

Woher Nutzlasten kommen, und was H4sI und eJ sind

Komprimierte Bytes erreichen Entwickler in wenigen Formen: als Rumpf einer HTTP-Antwort, deren Content-Encoding gzip oder deflate nennt, als Datei oder als Text, geschrieben als Base64 oder als Hexadezimalcode. Jeder gzip-Stream beginnt mit denselben drei Bytes, 1f 8b 08 — seinen zwei Signaturbytes und der Nummer seiner Kompressionsmethode, die DEFLATE ist —, und drei Bytes sind genau vier Zeichen Base64, also beginnt gzip, als Base64 geschrieben, immer mit H4sI. Das übergibt ein Abonnement von CloudWatch Logs einer Lambda-Funktion oder einem Kinesis-Stream in den Daten jedes Datensatzes, und so speichert Helm ein Release: sein JSON mit gzip komprimiert und dann als Base64 geschrieben. Wo Helm ein Release in einem Secret von Kubernetes ablegt, ergibt das direkte Lesen aus dem Secret Base64 in Base64, da ein Secret seine Daten ebenfalls als Base64 hält; das Base64-Werkzeug decodiert diese äußere Schicht zu dem H4sI…, das diese Seite liest.

Die zwei Header-Bytes von zlib nennen seine Methode, sein Fenster und die Stufe, auf der es geschrieben wurde, also beginnt sein Base64 bei der standardmäßigen Fenstergröße von zlib auf eine von vier Arten: eJ auf der Standardstufe — das schreibt zlib.compress von Python, sofern nichts anderes verlangt wird —, eA oder eF auf den Stufen darunter und eN auf denen darüber. Rohes deflate hat überhaupt keine Signatur, und sein Base64 beginnt so, wie sein erster Block gerade beginnt.

„Komprimiert als“ bestimmt, wie die Seite diese Bytes als Text liest und schreibt: als Base64, worauf die Seite voreingestellt ist, oder als Hexadezimalcode. Es gilt in beiden Richtungen, und die Seite ändert es nie an Ihrer Stelle, denn jede Hexadezimalziffer ist auch ein Zeichen von Base64, also ist 1f8b0800 in beiden Text und in jedem andere Bytes. Base64 wird im Standardalphabet oder im URL-sicheren gelesen, mit oder ohne seine Auffüllung, über Leerzeichen und Zeilenumbrüche hinweg, und mit Auffüllung geschrieben, oder URL-sicher ohne Auffüllung, wenn der Schalter „URL-sicher“ eingeschaltet ist. Hexadezimalcode wird in Paaren mit Leerzeichen dazwischen geschrieben und mit Leerzeichen oder zusammengeschrieben gelesen, mit 0x davor oder als Hexdump — und mit 0x schreibt T-SQL einen Binärwert, etwa das gzip, das COMPRESS() in SQL Server zurückgibt. Wenn Hexadezimalcode, als Base64 gelesen, scheitert oder Base64, als Hexadezimalcode gelesen, an einem Zeichen abgelehnt wird, das Hexadezimalcode nicht hat, und sich in beiden Fällen der ganze Text auf die andere Art lesen lässt, sagt das ein Hinweis neben einer Schaltfläche, die umschaltet; umgeschaltet wird erst, wenn Sie sie drücken.

Einen Stream lesen: Member, die Bytes danach, ein vorzeitiges Ende

RFC 1952 macht eine gzip-Datei zu einer Reihe von Membern: vollständigen gzip-Streams, einer nach dem anderen, jeder mit eigenem Header und Trailer. cat a.gz b.gz erzeugt eine solche Datei, und ebenso das Anhängen an eine mit gzip -c file >> archive.gz, das eigene Beispiel des Handbuchs von GNU gzip; gunzip liest alle Member und fügt zusammen, was sie enthalten. Diese Seite liest sie genauso: Jeder Member wird gelesen und geprüft, was sie enthalten, wird zusammengefügt gezeigt, und ein Hinweis sagt, wie viele gelesen wurden. Das tut nicht jedes Programm. Der Standard, dem der Dekompressor des Browsers selbst folgt, erlaubt in einem gzip-Stream nur einen Member und behandelt einen zweiten als Fehler, und zlib.decompress von Python hört nach dem ersten stillschweigend auf.

Bytes nach dem Ende, die keinen Member beginnen, werden übergangen statt abgelehnt, da alles davor vollständig und geprüft ist, und ein Hinweis sagt, wie viele es sind, bei welchem Versatz sie beginnen und ob jedes davon null ist. Nullen dort sind Auffüllung — das Handbuch von GNU gzip begegnet ihnen auf Band, bis zum Ende eines Blocks geschrieben —, und gunzip übergeht sie stillschweigend; andere Bytes übergeht es mit einer Warnung, dass nachfolgender Datenmüll ignoriert wurde, wo gzip.decompress von Python die Datei ablehnt. Dasselbe gilt nach der Adler-32-Prüfsumme eines zlib-Streams und nach dem letzten Block eines rohen Streams, auch wenn rohes deflate keine Prüfsumme trägt, die sich prüfen ließe.

Ein Stream, der vor seinem Ende aufhört, etwa ein beim Einfügen abgeschnittener Text oder ein auf halbem Weg abgebrochener Download, wird so weit gezeigt, wie er reicht, mit einem Hinweis, dass die Prüfsumme nicht geprüft wurde. Ein beschädigter Stream wird abgelehnt, und nichts davon wird gezeigt. Das ist strenger als gunzip, das ausgibt, was es dekomprimiert hat, bevor es die Prüfsumme erreicht, die es als falsch erkennt; doch ein geändertes Bit kann eine Weile stillschweigend decodiert werden, bevor irgendetwas es zeigt, also kann schon falsch sein, was herauskam, bevor der Schaden sichtbar wurde. Auch eine Position wird nicht angegeben, da die Stelle, an der ein Decoder einen Schaden bemerkt, nicht die Stelle des Schadens ist. Und ein zlib-Stream, der ein voreingestelltes Wörterbuch verlangt, wird mit einem eigenen Satz abgelehnt: Der Stream identifiziert die Bytes, mit denen sein Kompressor vorab geladen wurde, bringt sie aber nicht mit, also gibt es nichts, womit er sich lesen ließe.

Was herauskommt, wird so geschrieben, wie „Bytes als“ eingestellt ist: als Text in UTF-8 oder als Hexadezimalcode. Oft ist es JSON, das der JSON-Formatierer verschönert und validiert, wobei er auf die Zeile und die Spalte zeigt, an der es bricht. Ein mit gzip komprimiertes Bild oder Archiv ist kein Text, also wird bei „Text“ jede Folge seiner Bytes, die kein UTF-8 ist, als U+FFFD angezeigt, mit einem Hinweis, der diese Folgen zählt, neben einer Schaltfläche, die die Bytes als Hexadezimalcode zeigt; „Herunterladen“ speichert in jedem Fall die Bytes selbst. Eine Zeile unter dem Ergebnis zeigt, was der Header des ersten Members speichert: einen Namen, eine Zeit in UTC und einen Kommentar, und der gespeicherte Name benennt nie die Datei, die „Herunterladen“ speichert. Eine Ausgabe, die über das hinausgeht, was die Seite höchstens auf einmal dekomprimiert, endet dort, mit einem Hinweis, der diese Größe nennt, und wo der Trailer eines gzip-Streams eine Länge darüber angibt, sagt die Seite das, während die Arbeit läuft.

Warum eine kleine Eingabe wächst, und was Base64 hinzufügt

Kompression gewinnt, indem sie Wiederholungen findet, und ein kurzer Text hat wenig davon, während jeder Wrapper eigene Bytes kostet: Header und Trailer von gzip kommen auf 18 Bytes, wenn der Header nichts weiter speichert, die von zlib auf 6, und rohes deflate hat keine, auch wenn DEFLATE ein paar Bits darauf verwendet, seine Blöcke abzugrenzen. So wird Hello, world!, 13 Bytes, als gzip zu 33 Bytes, als zlib zu 21 und als rohes deflate zu 15. Die Größenzeile unter einem Ergebnis zählt die Bytes auf beiden Seiten und die Änderung zwischen ihnen, +153,8 % für dieses gzip, und ein Hinweis sagt, warum, wenn ein Ergebnis wächst. Text mit vielen Wiederholungen, etwa JSON oder ein Log, holt die Kosten um ein Vielfaches wieder herein: Das Beispiel, mit dem die Seite öffnet, ein kleines JSON, kommt auf weniger als die Hälfte seiner Größe.

Als Text geschrieben, kosten die Bytes noch einmal mehr. Base64 braucht vier Zeichen für je drei Bytes, ein Drittel mehr, wie der Leitfaden des Base64-Werkzeugs erklärt, also sind diese 33 Bytes gzip 44 Zeichen; Hexadezimalcode braucht zwei Ziffern pro Byte, und die Seite schreibt sie in Paaren mit Leerzeichen dazwischen, 98 Zeichen für dieselben 33 Bytes. Die Größenzeile zählt auch diese Zeichen, und der Datengrößen-Umrechner rechnet jede ihrer Zahlen in KB oder KiB um.

Komprimieren: keine Kompressionsstufe, nicht die Bytes von gzip -c, und kein Brotli

Das Komprimieren erledigt der Browser selbst, über den CompressionStream, den der Compression-Standard definiert, und dieser Standard bietet keine Kompressionsstufe an, also tut es diese Seite auch nicht: Sie bekommen die Stufe, die der Browser standardmäßig verwendet. gzip -9 fällt vielleicht etwas kleiner aus, selten aber um viel.

Auch sind die Bytes nicht die, die gzip -c schreibt, obwohl beide zum selben Text dekomprimieren. Zuerst unterscheiden sich die Header. GNU gzip speichert den Namen und die Zeit einer Datei, sofern man ihm nicht -n angibt, und aus einer Pipe setzt es die Zeit auf null; es vermerkt in einem Byte des Headers, ob -9 oder -1 angegeben war; und in das zehnte Byte, das RFC 1952 dem Betriebssystem zuweist, schreibt es unter Linux 03, die Nummer von Unix. Der Browser bekommt nur die Bytes, also kann weder der Name Ihrer Datei noch ihre Zeit mit dem Ergebnis hinausgelangen: Chromium speichert keinen Namen und setzt die Zeit auf null, was gzip -n wählt, und in das zehnte Byte schreibt es unter Linux 03, wo Chrome unter Windows 0a schreibt. Dann unterscheiden sich die komprimierten Daten, da der Kompressor von GNU gzip nicht der des Browsers ist: Bei einem langen Text wählen die beiden auf derselben Stufe verschiedene Bytes. Eine Abweichung von gzip -c bedeutet daher für sich nichts; was zählt, ist, dass beide zu denselben Bytes dekomprimieren.

Brotli gibt es hier vorerst nicht. Der Compression-Standard nennt es, aber noch schreibt es nicht jeder Browser, und eine Wahl, die die Seite nur dort anbieten könnte, wo der Browser sie beherrscht, wäre in verschiedenen Browsern eine andere Seite. zstd steht überhaupt nicht in diesem Standard. Was diese Seite liest und schreibt, ist DEFLATE in seinen drei Wrappern, und sonst nichts.

Eine Datei hinein, eine Datei heraus, und nichts hochgeladen

Eine Datei kann in beiden Richtungen die Stelle des Textfelds einnehmen: Wählen Sie eine oder legen Sie sie auf dem Feld ab. Ihre Bytes werden so gelesen, wie sie sind, unabhängig von „Komprimiert als“ und „Bytes als“, und eine Datei hat hier keine Grenze, wo das Textfeld eine hat, auch wenn eine sehr große Datei eine Warnung auslöst, dass der Browser sie womöglich nicht bewältigt. Beim Dekomprimieren benennt „Herunterladen“ das Ergebnis so, wie gunzip es tut: den Namen der Datei ohne .gz, wobei .tgz zu .tar wird, und ebenso ohne .zz oder .deflate, die Endungen, die diese Seite für die anderen beiden Wrapper schreibt; jeder andere Name und alles Eingefügte kommt als bytes.bin heraus. Beim Komprimieren hängt es .gz, .zz oder .deflate an den Namen der Datei an — .zz ist die Endung, mit der pigz zlib benennt — oder, für das, was Sie eingetippt haben, an das Wort text.

„Als Eingabe verwenden“ verschiebt ein Ergebnis in das Feld und kehrt die Richtung um, sodass ein Hin- und Rückweg einen Klick kostet, und ein Ergebnis, das aus einer Datei kam oder zu groß für das Feld ist, wandert als Datei. In welche Richtung es auch geht, die Datei wird in diesem Tab gelesen, und die Arbeit läuft in einem Worker, den die Seite auf Ihrem eigenen Gerät startet: Dafür wird weder eine Nutzlast noch eine Datei hochgeladen.

Dasselbe mit gunzip und Python

Auf der Kommandozeile lesen gunzip und zcat gzip so, wie diese Seite es tut, jeden Member eingeschlossen, sobald base64 -d eine Nutzlast wieder in Bytes verwandelt hat, auch wenn keines von beiden zlib oder rohes deflate liest, was beide als nicht gzip zurückweisen; gzip -k komprimiert eine Datei und behält das Original daneben. In Python liest gzip.decompress den Wrapper von gzip und jeden Member darin, und zlib.decompress liest jeden der drei Wrapper, wobei wbits ihm sagt, welchen:

# Eine Nutzlast: erst decodieren, dann dekomprimieren
printf 'H4sIAAAAAAAAA/NIzcnJ11Eozy/KSVEEAObG5usNAAAA' | base64 -d | gunzip   # Hello, world!

# Eine Datei: komprimieren und das Original behalten, dann wieder ausgeben
printf 'Hello, world!' > hello.txt
gzip -k hello.txt
zcat hello.txt.gz                                  # Hello, world!

# Zwei Member, der zweite an den ersten angehängt, als einer zurückgelesen
printf 'Hello, ' | gzip > two.gz
printf 'world!' | gzip >> two.gz
zcat two.gz                                        # Hello, world!

# Dasselbe in einem Skript: jeder Member, dann ein Wrapper nach dem anderen
import base64, gzip, zlib
two = open('two.gz', 'rb').read()
zl = base64.b64decode('eJzzSM3JyddRKM8vyklRBAAgXgSK')
raw = base64.b64decode('80jNycnXUSjPL8pJUQQA')
gzip.decompress(two)                               # b'Hello, world!'
zlib.decompress(two, wbits=31)                     # b'Hello, '
zlib.decompress(zl, wbits=15)                      # b'Hello, world!'
zlib.decompress(raw, wbits=-15)                    # b'Hello, world!'
zlib.decompress(zl, wbits=47)                      # b'Hello, world!'

Mit wbits verlangt 31 den Wrapper von gzip, 15, der Standardwert, den von zlib, und -15 rohes deflate — die 15 ist dabei jeweils das größte Fenster —, während 47 den Wrapper von gzip oder von zlib nimmt, je nachdem, womit die Bytes beginnen. zlib.decompress liest allerdings einen einzigen Member und verwirft den Rest stillschweigend, weshalb es von den beiden Membern oben nur b'Hello, ' zurückgibt; gzip.decompress liest sie alle. Python ist außerdem in zweifacher Hinsicht strenger als diese Seite: gzip.decompress lehnt Bytes nach dem Ende ab, die keine Nullen sind, und beide Funktionen lehnen einen Stream ab, der vorzeitig endet, wo die Seite zeigt, was herauskam.

Häufig gestellte Fragen

Warum ist mein komprimiertes Ergebnis größer als das, was ich eingetippt habe?
Weil jeder Wrapper eigene Bytes kostet und ein kurzer Text wenig hat, was die Kompression entfernen kann: gzip fügt 18 Bytes hinzu, zlib 6, und DEFLATE grenzt außerdem seine Blöcke ab, also wachsen ein paar Wörter, wo ein langes JSON schrumpft, und die Seite sagt das in einem Hinweis. Rohes deflate ist das kleinste der drei. Als Base64 geschrieben, ist jedes Ergebnis noch einmal um ein Drittel länger.
Warum stimmt die Ausgabe nicht mit gzip -c überein?
Nichts verspricht, dass sie es tut. Ein gzip-Header kann einen Namen, eine Zeit, eine Kennzeichnung der Stufe und ein Byte für das Betriebssystem enthalten, die GNU gzip und der Browser verschieden ausfüllen, und die beiden sind verschiedene Kompressoren, also können sich bei einem längeren Text sogar die Daten zwischen Header und Trailer unterscheiden. Zwei gzip-Streams eines Textes sind beide richtig, wenn jeder zu ihm dekomprimiert, also vergleichen Sie, wozu sie dekomprimieren: „Als Eingabe verwenden“ schickt das Ergebnis dieser Seite direkt in die Gegenrichtung.
Was bedeutet H4sI am Anfang einer Nutzlast?
Dass die Nutzlast gzip ist, als Base64 geschrieben: Jeder gzip-Stream beginnt mit den Bytes 1f 8b 08, und diese drei Bytes sind in Base64 H4sI. Fügen Sie sie hier ein, wie sie ist. Eine Nutzlast, die mit eJ beginnt, ist höchstwahrscheinlich zlib auf seiner Standardstufe, und eine, die mit keinem von beiden beginnt, kann rohes deflate oder gar nicht komprimiert sein, was Ihnen die Seite sagt.
Ist gzip eine Verschlüsselung?
Nein. Kompression braucht keinen Schlüssel, also kann jeder, der die Bytes hat, sie dekomprimieren, hier oder mit gunzip, und jedes Byte kommt zurück. Ein Passwort in einer mit gzip komprimierten Nutzlast liegt so offen wie eines, das ausgeschrieben dasteht.
Wird meine Nutzlast oder meine Datei irgendwohin hochgeladen?
Nein. Die Nutzlast, die Sie einfügen, und die Datei, die Sie wählen oder ablegen, werden beide in diesem Tab gelesen, wo ein Worker, den die Seite startet, das Komprimieren und das Dekomprimieren erledigt, und „Herunterladen“ erzeugt seine Datei gleich dort aus dem Ergebnis. Keines von beiden wird an diese Website oder an sonst jemanden gesendet.

Verwandte Werkzeuge

  • JSON-Formatierer

    Validiert und verschönert JSON, mit klaren Fehlerstellen.

  • Datengrößen-Umrechner

    Rechnen Sie zwischen KB, MB, GB und KiB, MiB, GiB um.

  • Base64

    Codiert und decodiert Base64 – volle UTF-8-Unterstützung.

  • Base32

    Codiert und decodiert Base32 und base32hex — Text in UTF-8 oder Bytes als Hexadezimalcode.