Problem mit zipfile

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
HarryH
User
Beiträge: 266
Registriert: Freitag 23. Mai 2003, 09:08
Wohnort: Deutschland

Dienstag 5. September 2006, 10:10

Hallo,

Ich habe ein Problem mit dem Modul zipfile und großen Dateien in Ziparchivs (> 500MB)
Beim lesen dieser Dateien mit der Methode read() erhalte ich folgenden Fehler:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Dokumente und Einstellungen\hh16534\Eigene Dateien\Python\Module\hhZip.py", line 219, in Unzip
    dezip = self.zipfile.read(self.name_dic[filename])
  File "d:\python24\lib\zipfile.py", line 357, in read
    bytes = dc.decompress(bytes)
MemoryError
Anscheinend läuft der Speicher voll?
Wie kann ich diesen Fehler beheben?
Gruß, Harry
BlackJack

Dienstag 5. September 2006, 20:09

Gar nicht. Oder zumindest nicht ohne das `zipfile` Modul zu erweitern. Das liest immer nur komplette Dateien ein. Was ungünstig wird, wenn die Datei gross ist.
HarryH
User
Beiträge: 266
Registriert: Freitag 23. Mai 2003, 09:08
Wohnort: Deutschland

Mittwoch 6. September 2006, 07:30

Hallo,

Ich bin mittlerweile auch darauf gekommen, das es nur dadurch möglich ist das zifpile-Modul zu erweitern.
Ich habe die Methode 'zipfile.read' etwas umgebaut.
Folgende Methode kann in eine geerbte Klasse von zipfile.ZipFile übernommen werden. Sie dient dazu, auch große Files eines Archivs mühelos zu entpacken. Der File-Buffer wird dabei nicht komplett dekomprimiert sondern schrittweise.

Code: Alles auswählen

def unzip(self, name, dir):
    """Unzip file and write to defined directory"""
    #Checking
    if self.mode not in ("r", "a"):
        raise RuntimeError, 'read() requires mode "r" or "a"'
    if not self.fp:
        raise RuntimeError, \
              "Attempt to read ZIP archive that was already closed"
    #Filename and dir creation
    fname = os.path.join(dir, os.path.basename(name))
    if not os.path.exists(dir):
        os.makedirs(dir)

    #Jump to right archiv position and set bufsize
    zinfo = self.getinfo(name)
    filepos = self.fp.tell()
    self.fp.seek(zinfo.file_offset, 0)
    if zinfo.compress_size > 1024:
        bufsize = zinfo.compress_size / 50
    else:
        bufsize = zinfo.compress_size

    #Start unzip operation
    if zinfo.compress_type == zipfile.ZIP_STORED:
        #Read bytes from zip archiv name and writing
        crc = 0
        f = file(fname, "wb")
        while f.tell() < zinfo.compress_size:
            if bufsize > zinfo.compress_size - f.tell():
                bufsize = zinfo.compress_size - f.tell()
            bytes = self.fp.read(bufsize)
            f.write(bytes)
            crc = binascii.crc32(bytes, crc)
        self.fp.seek(filepos, 0)
        f.close()
    elif zinfo.compress_type == zipfile.ZIP_DEFLATED:
        #Read bytes from zip archiv name
        bytes = self.fp.read(zinfo.compress_size)
        self.fp.seek(filepos, 0)
        #Checking
        if not zlib:
            raise RuntimeError, \
                  "De-compression requires the (missing) zlib module"
        #Decopmressing and writing
        dc = zlib.decompressobj(-15)
        crc = 0
        f = file(fname, "wb")
        while 1:
            bytes = dc.decompress(bytes, bufsize)
            f.write(bytes)
            crc = binascii.crc32(bytes, crc)
            if dc.unconsumed_tail:
                bytes = dc.unconsumed_tail
            else:
                break
        #Need to feed in unused pad byte so that zlib won't choke
        ex = dc.decompress('Z') + dc.flush()
        if ex:
            f.write(ex)
            crc = binascii.crc32(ex, crc)
        f.close()
    else:
        raise zipfile.BadZipfile, \
              "Unsupported compression method %d for file %s" % \
        (zinfo.compress_type, name)
    #Check CRC
    if crc != zinfo.CRC:
        raise zipfile.BadZipfile, "Bad CRC-32 for file %s" % name

    #Set orginal file date anf file attribute
    mtime = time.mktime(zinfo.date_time + (-1, -1, -1))
    os.utime(fname, (time.time(), mtime))
Vielleicht hat ja jemand noch Verbesserungsvorschläge!?
Gruß, Harry
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Mittwoch 6. September 2006, 11:22

HarryH hat geschrieben:Vielleicht hat ja jemand noch Verbesserungsvorschläge!?
Hast du es auch mit Monkey-Patching in die Originalklasse versucht statt eine Klasse abzuleiten?
My god, it's full of CARs! | Leonidasvoice vs Modvoice
HarryH
User
Beiträge: 266
Registriert: Freitag 23. Mai 2003, 09:08
Wohnort: Deutschland

Mittwoch 6. September 2006, 11:27

Hi Leonidas,

Was ist Monkey-Patching? Das habe ich noch nie gehört :oops:
Gruß, Harry
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Mittwoch 6. September 2006, 11:34

HarryH hat geschrieben:Was ist Monkey-Patching? Das habe ich noch nie gehört :oops:
Wird hier am Beispiel von Zope erklärt.

In deinem Fall würe das etwa so aussehen:

Code: Alles auswählen

import zipfile
zipfile.ZipFile.unzip = unzip
Oder du kannst read() überschrieben, dass es nicht an MemoryErrors leidet und auch wieder mit Monkey Patching arbeiten.

Achja, manche meinen das Monkey Patching unsauber ist - aber ich finde es eigentlich in Massen eigentlich ganz akzeptabel.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
HarryH
User
Beiträge: 266
Registriert: Freitag 23. Mai 2003, 09:08
Wohnort: Deutschland

Mittwoch 6. September 2006, 11:46

Hi,

Bedeutet

Code: Alles auswählen

zipfile.ZipFile.unzip = unzip
das der Klasse zipfile.ZipFile die Methode unzip hinzugefügt wird?

Wenn ja, was ist der bedeutende Unterschied dazu, wenn ich von zipfile.ZipFile erbe?
Gruß, Harry
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Donnerstag 7. September 2006, 10:12

Je, genau, das bedeutet es.
HarryH hat geschrieben:Wenn ja, was ist der bedeutende Unterschied dazu, wenn ich von zipfile.ZipFile erbe?
In diesem Fall ist der Unterschied nicht so relevant. Aber stell dir vor, du modifizierst read() so, dass es out-of-the-box mit großen ZIP-Dateien auskommt. Dann injizierst du diesen Monkey-Patch in die ZipFile-Klasse. Und sofort können alle Skripte durch diesen Monkey-Patch _sofort_ mit großen ZIP-Dateien umgehen, ohne dass man noch irgendetwas ändern müsste.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
BlackJack

Donnerstag 7. September 2006, 15:22

Das klingt ein bischen übertrieben. Nicht alle Skripte sondern nur alle Module in Programmen die vor dem Benutzen des `zipfile` Moduls den Monkey-Patch importiert haben.
Antworten