Question

Je suis une base de données et l'interrogation archivage des résultats en utilisant Python, et je suis en train de compresser les données que je l'écris aux fichiers journaux. Je vais avoir quelques problèmes avec, cependant.

Mon apparence de code comme ceci:

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))

Cependant, mon fichier de sortie a une taille de 1.409.780. Exécution bunzip2 sur les résultats de fichiers dans un fichier avec une taille de 943634, et en cours d'exécution sur bzip2 qui se traduit par une taille de 217275. En d'autres termes, le fichier non compressé est nettement plus petit que le fichier compressé en utilisant le codec bzip Python. Est-il possible de résoudre ce problème, autre que la course bzip2 sur la ligne de commande?

J'ai essayé gzip Python codec (changer la ligne à codecs.open(archive_file, 'a+', 'zip')) pour voir si elle a résolu le problème. Je reçois toujours de gros fichiers, mais je reçois aussi une erreur de gzip: archive_file: not in gzip format lorsque je tente de décompresser le fichier. Qu'est-ce qui se passe là-bas?


EDIT : J'ai eu à l'origine le fichier ouvert en mode append, pas en mode écriture. Même si cela peut ou peut ne pas être un problème, la question tient toujours si le fichier est ouvert dans « w » le mode.

Était-ce utile?

La solution

Comme d'autres l'ont noté, la question est que la bibliothèque codecs doesn « t utiliser un codeur incrémental pour coder les données; Au contraire, il code pour chaque fragment de données introduites dans le procédé de write sous forme de bloc compressé. Ceci est horriblement inefficace, et juste une terrible décision de conception d'une bibliothèque conçue pour fonctionner avec des flux.

L'ironie est qu'il ya un codeur incrémental bz2 parfaitement raisonnable déjà construit en Python. Il est difficile de ne pas créer un « fichier comme » classe qui fait automatiquement la bonne chose.

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')

Une mise en garde : Dans cet exemple, j'ai ouvert le fichier en mode append; annexant plusieurs flux compressés dans un seul fichier fonctionne parfaitement bien avec bunzip2, mais Python lui-même ne peut pas le manipuler (bien qu'il y ait est un patcher pour elle). Si vous avez besoin de lire les fichiers compressés que vous créez de nouveau dans Python, bâton à un seul flux par fichier.

Autres conseils

Le problème semble être que la production est en cours d'écriture sur chaque write(). Cela provoque chaque ligne à comprimer dans son propre bloc bzip.

Je voudrais essayer la construction d'une beaucoup plus grande chaîne (ou une liste de chaînes si vous êtes inquiet au sujet du rendement) en mémoire avant de l'écrire sur le fichier. Une bonne taille à tirer pour serait 900K (ou plus) que c'est la taille du bloc que les utilisations bzip2

Le problème est dû à l'utilisation du mode append, dont les résultats dans des fichiers qui contiennent plusieurs blocs de données compressées. Regardez cet exemple:

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

Sur mon système, cela produit un fichier de 12 octets. Voyons voir ce qu'il contient:

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

Bon, maintenant, nous allons faire une autre écriture en mode append:

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

Le fichier est maintenant 24 octets, et son contenu sont:

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

Qu'est-ce qui se passe ici est que attend Décompresser un seul flux compressé. Vous devez vérifier les spécifications pour voir ce que le comportement officiel est avec plusieurs flux chaînées, mais dans mon expérience qu'ils traitent le premier et ignorer le reste des données. C'est ce que fait Python.

Je pense que bunzip2 fait la même chose. Donc, en réalité votre fichier est compressé, et est beaucoup plus petite que les données qu'il contient. Mais lorsque vous l'exécutez par bunzip2, vous revenir seul le premier jeu d'enregistrements que vous lui a écrit; le reste est mis au rebut.

Je ne sais pas comment cela est différent de la codecs façon de le faire, mais si vous utilisez GzipFile à partir du module gzip vous pouvez progressivement append au fichier, mais il ne va pas compresser très bien, sauf si vous écrivez de grandes quantités de données à un moment (peut-être> 1 Ko). Ceci est juste la nature des algorithmes de compression. Si les données que vous écrivez n'est pas super important (à savoir que vous pouvez faire face à la perdre si votre processus meurt), alors vous pouvez écrire une classe GzipFile tampon enveloppant la classe importée qui écrit plus gros morceaux de données.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top