Frage

Ich wende eine Datenbank ab und archiviere die Ergebnisse mit Python und versuche, die Daten zu komprimieren, während ich sie in die Protokolldateien schreibe. Ich habe jedoch einige Probleme damit.

Mein Code sieht so aus:

log_file = codecs.open(archive_file, 'w', 'bz2')
for id, f1, f2, f3 in cursor:
    log_file.write('%s %s %s %s\n' % (id, f1 or 'NULL', f2 or 'NULL', f3))

Meine Ausgabedatei hat jedoch eine Größe von 1.409.780. Betrieb bunzip2 In der Datei führt eine Datei mit einer Größe von 943.634 und ausgeführt bzip2 Dies führt zu einer Größe von 217.275. Mit anderen Worten, die unkomprimierte Datei ist erheblich kleiner als die Datei, die mit dem BZIP -Codec von Python komprimiert wird. Gibt es eine Möglichkeit, dies zu beheben, außer zu laufen? bzip2 in der Befehlszeile?

Ich habe Pythons GZIP -Codec ausprobiert (ändern Sie die Linie auf codecs.open(archive_file, 'a+', 'zip')) um zu sehen, ob es das Problem behoben hat. Ich bekomme immer noch große Dateien, aber ich bekomme auch eine gzip: archive_file: not in gzip format Fehler, wenn ich versuche, die Datei zu komprimieren. Was ist da los?


BEARBEITEN: Ich hatte ursprünglich die Datei im Append -Modus geöffnet, nicht den Schreibmodus. Dies kann zwar ein Problem sein oder nicht, aber die Frage gilt dennoch, wenn die Datei im 'W' -Modus geöffnet ist.

War es hilfreich?

Lösung

Wie andere Poster festgestellt haben, ist das Problem, dass die codecs Die Bibliothek verwendet keinen inkrementellen Encoder, um die Daten zu codieren. Stattdessen codiert es jeden Snippet von Daten, die dem an die gefüttert werden write Methode als komprimierter Block. Dies ist schrecklich ineffizient und nur eine schreckliche Designentscheidung für eine Bibliothek, die mit Streams zusammenarbeiten soll.

Die ironische Sache ist, dass es bereits einen absolut vernünftigen inkrementellen BZ2 -Encoder in Python gibt. Es ist nicht schwierig, eine "fileähnliche" Klasse zu erstellen, die automatisch die richtige Sache macht.

import bz2

class BZ2StreamEncoder(object):
    def __init__(self, filename, mode):
        self.log_file = open(filename, mode)
        self.encoder = bz2.BZ2Compressor()

    def write(self, data):
        self.log_file.write(self.encoder.compress(data))

    def flush(self):
        self.log_file.write(self.encoder.flush())
        self.log_file.flush()

    def close(self):
        self.flush()
        self.log_file.close()

log_file = BZ2StreamEncoder(archive_file, 'ab')

Eine Einschränkung: In diesem Beispiel habe ich die Datei im Append -Modus geöffnet. Das Anhängen mehrerer komprimierter Streams an eine einzelne Datei funktioniert perfekt mit bunzip2, aber Python selbst kann nicht damit umgehen (obwohl es da ist ist ein Patch dafür). Wenn Sie die komprimierten Dateien lesen müssen, die Sie wieder in Python erstellen, halten Sie sich an einen einzelnen Stream pro Datei.

Andere Tipps

Das Problem scheint zu sein, dass die Ausgabe auf jeden geschrieben wird write(). Dies führt dazu, dass jede Linie in ihrem eigenen BZIP -Block komprimiert wird.

Ich würde versuchen, eine viel größere Zeichenfolge (oder eine Liste der Zeichenfolgen zu erstellen, wenn Sie sich Sorgen um die Leistung machen) im Speicher, bevor Sie sie in die Datei schreiben. Eine gute Größe für das Schießen wäre 900k (oder mehr), da dies die Blockgröße ist, die BZIP2 verwendet

Das Problem ist auf die Verwendung des Append -Modus zurückzuführen, der zu Dateien führt, die mehrere komprimierte Datenblöcke enthalten. Sehen Sie sich dieses Beispiel an:

>>> import codecs
>>> with codecs.open("myfile.zip", "a+", "zip") as f:
>>>     f.write("ABCD")

Auf meinem System werden eine Datei 12 -Bytes in Größe erzeugt. Mal sehen, was es enthält:

>>> with codecs.open("myfile.zip", "r", "zip") as f:
>>>     f.read()
'ABCD'

Okay, jetzt machen wir einen weiteren Schreiben im Append -Modus:

>>> with codecs.open("myfile.zip", "a+", "zip") as f:
>>>     f.write("EFGH")

Die Datei hat jetzt 24 Bytes und ihr Inhalt lauten:

>>> with codecs.open("myfile.zip", "r", "zip") as f:
>>>     f.read()
'ABCD'

Was hier passiert, ist, dass Unzip einen einzigen Reißverschlussstrom erwartet. Sie müssen die Spezifikationen überprüfen, um zu sehen, was das offizielle Verhalten mit mehreren verketteten Streams ist, aber meiner Erfahrung nach verarbeiten sie die erste und ignorieren den Rest der Daten. Das macht Python.

Ich gehe davon aus, dass Bunzip2 dasselbe tut. In Wirklichkeit ist Ihre Datei also komprimiert und ist viel kleiner als die von ihnen enthaltenden Daten. Aber wenn Sie es durch Bunzip2 ausführen, erhalten Sie nur die ersten Datensätze, die Sie daran geschrieben haben. Der Rest wird verworfen.

Ich bin mir nicht sicher, wie unterschiedlich dies von der Codecs -Art ist, dies zu tun, aber wenn Sie GZIPFile vom GZIP -Modul verwenden Zeit (vielleicht> 1 kb). Dies ist nur die Natur der Kompressionsalgorithmen. Wenn die Daten, die Sie schreiben, sind nicht super wichtig (dh Sie können sich damit befassen, sie zu verlieren, wenn Ihr Prozess stirbt), können Sie eine gepufferte GZIPFILE -Klasse schreiben, die die importierte Klasse einpackt, die größere Datenbrocken hervorruft.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top