GZIPOutputStream vs. 'echtes' gzip

Hallo zusammen.

Ich möchte Dateien mit dem GZIPOutputStream aus java.util.zip packen, was ja eigentlich genauso ablaufen und das gleiche Ergebnis liefern sollte die das „echte“ gzip. Problem: das tut es nicht (bezogen auf die Größe der gepackten Datei), was für mich aber sehr wichtig wäre.

Wenn ich einen konkreten String (Länge 44) den ich vorher in eine Datei gepackt habe - in der Methode „uncompressedFile“ - mit folgender Methode packe:

int read = 0;
byte[] data = new byte[1024];

try {
fileIn = new FileInputStream(uncompressedFile);
zipOut = new GZIPOutputStream(new FileOutputStream(compressedFile));

while((read = fileIn.read(data, 0, 1024)) != -1) {
zipOut.write(data, 0, read);
}

fileIn.close();
zipOut.close();
} catch…

und mir dann die Größe der gepackten Datei ausgeben lassen, wird eine Größe von 39B ausgegeben.

Jetzt packe ich die gleiche Ausgangsdatei mit dem selben String als Inhalt mit gzip und erhalte unabhängig der gewählten Kompressionsstufe (1-9) immer eine Datei mit der Größe 51B.

Sollten die Dateigrößen nicht identisch sein, wenn nach dem selben Verfahren gepackt wird?

Wenn ich versuche, den String direkt mit Hilfe des Deflaters aus java.util.zip zu packen (also direkt mit dem Algorithmus, den gzip angeblich benutzt), mit folgender Methode:

try {
byte[] input = string.getBytes(„UTF-8“);
byte[] output = new byte[32768];
this.deflater = new Deflater(1);
deflater.setInput(input);
deflater.finish();
int compressedDataLength = deflater.deflate(output);
System.out.println(compressedDataLength);

bekomme ich unabhängig vom Argument im Konstruktor des Deflaters (1-9, sollte wieder die Kompressionsstufe einstellen) stets die Größe 27B ausgegeben.

Ich vermute mal, die Ausgabe des Deflaters ist die kleinste, weil hier nicht noch der Overhead der gzip-Datei mit eingeht.

Bleibt die Frage: warum unterscheiden sich die Größen der Dateien, die gzip erzeugt von denen, die mit dem GZIPOutputStream erstellt werden?

Ich habe vorher noch nicht mit dem Package java.util.zip gearbeitet. Sollte mein Code ein paar Mängel haben, wäre ich auch da für Anregungen dankbar. Denn was in der ersten Methode das byte-Array macht, ist mir zum Beispiel nicht wirklich klar. Sollte man nicht auch bei dem GZIPOutputStream einstellen können, wie gut er komprimiert?

Hintergrund der ganzen Sache: die Größen der Dateien die gzip erstellt haben eine interessante Eigenschaft bezogen auf den Ursprung ihres Inhalts, so dass für mich genau diese Größen sehr wichtig sind und ich auch genau diese brauche. Natürlich könnte ich einfach einen Aufruf an gzip schicken - sowas finde ich aber immer sehr unschön, unelegant und überflüssig, wenn Java mir doch eigentlich alles geben sollte, was ich brauche.

Kurz: wie bekomme ich es hin, mit Java genau das gleiche zu machen wie gzip?

Ich hoffe, Ihr könnt mir mal wieder helfen :smile:

Gruß
Schorsch

Moien

und mir dann die Größe der gepackten Datei ausgeben lassen,
wird eine Größe von 39B ausgegeben.

Jetzt packe ich die gleiche Ausgangsdatei mit dem selben
String als Inhalt mit gzip und erhalte unabhängig der
gewählten Kompressionsstufe (1-9) immer eine Datei mit der
Größe 51B.

Welches gzip benutzt du ? Es gibt mehrere Implementierungen mit unterschiedlichen Optimierungen.

Hast du dir die Daten mal genauer mit einem HEX-editor angekuckt ? Speichert dein gzip evtl. eine Prüfsumme ?

byte[] input = string.getBytes(„UTF-8“);

An dieser Stelle solltest du die Länge der input-Arrays checken (input.length). Wer weiss in welchen Formaten du genau arbeitetes.

Bleibt die Frage: warum unterscheiden sich die Größen der
Dateien, die gzip erzeugt von denen, die mit dem
GZIPOutputStream erstellt werden?

Stell mal einen Beispielstring mit Grössen für dein gzip und dein GZIPOutputStream hier rein. Und die MD5summen der Dateien.

Ich habe vorher noch nicht mit dem Package java.util.zip
gearbeitet. Sollte mein Code ein paar Mängel haben, wäre ich
auch da für Anregungen dankbar.

Das sind die Beispielcodes aus der java-doc. Die sind schon OK.

cu

Okay, los gehts:

  • hier bei mir auf Suse 10.2 ist gzip 1.3.5

  • Beispiel-String: LALRRLRRRRLRRRRALLRRLALRRLRRRRLLRRRRLRRALLDA

  • Länge: 44 Zeichen

  • Länge des Arrays nach dem Aufruf „byte[] input = string.getBytes(„UTF-8“);“: 44

  • Größe der Datei gepackt mit gzip (alle Optionen auf default): 51B

  • MD5 der gzip Datei: 6d2f88a1bdd99b49cddfbec89eb5a5a1

  • Größe der Datei gepackt mit dem Stream: 39B

  • MD5 der Stream Datei: 28b57a6539091bed50a2fe87b415c72a

Gruß
Schorsch

HEX Editor
Das sagt der Editor zu der mit gzip gepackten Datei:

│00000000 1f 8b 08 08 e9 05 79 46-00 03 73 65 71 32 31 2e |???yF ?seq21.|
│00000010 74 78 74 00 f3 71 f4 09-0a 02 22 28 e1 e8 03 e2 |txt ?q???"(???|
│00000020 22 c4 60 32 40 71 17 47-05 2e 00 d3 4d 30 e2 2e |"?`2@q?G?. ?M0?.|
│00000030 00 00 00

Gruß
Schorsch

Moien

hier bei mir auf Suse 10.2 ist gzip 1.3.5

Das speichert den ursprünglichen Dateinamen mit in die .gz-Datei. Die java Variante kann das gar nicht tun, die kennt den ursprünglichen Namen nicht.

Größe der Datei gepackt mit dem Stream: 39B

Ich komm auf 40. Hast du ein newline mit drin ?

cu

Moien

Das speichert den ursprünglichen Dateinamen mit in die
.gz-Datei. Die java Variante kann das gar nicht tun, die kennt
den ursprünglichen Namen nicht.

Übrigens bringt mich

cat „datei“ | gzip > „datei.gz“

zu einer 100% gleichen Datei wie dein erster java-code.

cu

Hi.

hier bei mir auf Suse 10.2 ist gzip 1.3.5

Das speichert den ursprünglichen Dateinamen mit in die
.gz-Datei. Die java Variante kann das gar nicht tun, die kennt
den ursprünglichen Namen nicht.

Wenn ich also nur an der Größe des komprimierten String interessiert bin, ist die Methode mit Java die bessere?

Größe der Datei gepackt mit dem Stream: 39B

Ich komm auf 40. Hast du ein newline mit drin ?

Ich erstelle mit dem Stream ja eine gepackte Datei (bei mir die Variable „File compressedFile“), und auf deren Größe greife ich zu durch

compressedFile.length

Dabei gibt er mir immer 39 aus. In der unkomprimierten Datei ist kein Zeilenumbruch mit drin.

Gruß
Schorsch

Moien

Wenn ich also nur an der Größe des komprimierten String
interessiert bin, ist die Methode mit Java die bessere?

Der Deflater von java ist dafür die genauste Technik.

cu

Wenn ich also nur an der Größe des komprimierten String
interessiert bin, ist die Methode mit Java die bessere?

Der Deflater von java ist dafür die genauste Technik.

Größe des Strings mit dem Deflater gepackt: 27B

Leider funktioniert das, was ich machen will, mit den Werten aus dem Deflater nicht ganz so gut. Auf jeden Fall schonmal danke für Deine Hilfe - wie die letzten Male auch :smile:

Gruß
Schorsch