8

データベースにクエリを実行し、Python を使用して結果をアーカイブしています。ログ ファイルに書き込むときにデータを圧縮しようとしています。しかし、私はそれでいくつかの問題を抱えています。

私のコードは次のようになります。

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

ただし、出力ファイルのサイズは 1,409,780 です。このファイルで実行bunzip2すると、ファイルのサイズは 943,634 になり、そのファイルで実行すると、サイズは 217,275 になりbzip2ます。つまり、圧縮されていないファイルは、Python の bzip コーデックを使用して圧縮されたファイルよりも大幅に小さくなります。 コマンドラインで実行する以外に、これを修正する方法はありますか?bzip2

問題が解決するかどうかを確認するために、Python の gzip コーデック (行を に変更codecs.open(archive_file, 'a+', 'zip')) を試しました。まだ大きなファイルを取得できますがgzip: archive_file: not in gzip format、ファイルを解凍しようとするとエラーも発生します。 何が起こっているのですか?


EDIT:私は元々、ファイルを書き込みモードではなく追加モードで開いていました。これは問題になる場合とそうでない場合がありますが、ファイルが 'w' モードで開かれている場合でも問題は残ります。

4

4 に答える 4

2

他の投稿者が指摘しているように、問題は、codecsライブラリがインクリメンタル エンコーダーを使用してデータをエンコードしていないことです。代わりに、メソッドに供給されるデータのすべてのスニペットをwrite圧縮ブロックとしてエンコードします。これは恐ろしく非効率的であり、ストリームを操作するように設計されたライブラリにとってはひどい設計上の決定です。

皮肉なことに、完全に合理的なインクリメンタル bz2 エンコーダーが既に Python に組み込まれています。正しいことを自動的に行う「ファイルのような」クラスを作成することは難しくありません。

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

警告: この例では、ファイルを追加モードで開いています。複数の圧縮されたストリームを単一のファイルに追加することは、 で完全に機能しますbunzip2が、Python 自体はそれを処理できません (ただし、パッチはあります)。作成した圧縮ファイルを Python に読み込む必要がある場合は、ファイルごとに 1 つのストリームに固執してください。

于 2010-09-30T14:46:07.910 に答える
1

問題は、出力がすべてに書き込まれていることのようですwrite()。これにより、各行が独自の bzip ブロックに圧縮されます。

ファイルに書き出す前に、はるかに大きな文字列 (パフォーマンスが心配な場合は文字列のリスト) をメモリに作成してみます。撮影に適したサイズは 900K (またはそれ以上) です。これは bzip2 が使用するブロック サイズです。

于 2010-09-29T21:28:11.907 に答える
0

これがコーデックの方法とどのように異なるのかはわかりませんが、gzip モジュールの GzipFile を使用すると、ファイルに段階的に追加できますが、一度に大量のデータを書き込んでいない限り、圧縮はうまくいきません。時間 (おそらく > 1 KB)。これは、圧縮アルゴリズムの性質にすぎません。書き込んでいるデータがそれほど重要でない場合 (つまり、プロセスが停止した場合にデータが失われることに対処できる場合) は、インポートされたクラスをラップするバッファリングされた GzipFile クラスを作成して、より大きなデータのチャンクを書き出すことができます。

于 2010-09-29T21:18:20.903 に答える
0

この問題は、複数の圧縮されたデータ ブロックを含むファイルになる追加モードの使用が原因です。この例を見てください:

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

私のシステムでは、これによりサイズが 12 バイトのファイルが生成されます。内容を見てみましょう:

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

さて、今度は追加モードで別の書き込みをしましょう:

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

ファイルのサイズは 24 バイトになり、その内容は次のとおりです。

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

ここで起こっていることは、unzip が 1 つの圧縮されたストリームを想定していることです。仕様を確認して、複数の連結ストリームでの公式の動作を確認する必要がありますが、私の経験では、最初のストリームが処理され、残りのデータは無視されます。それがPythonのすることです。

bunzip2 も同じことをしていると思います。したがって、実際にはファイルは圧縮されており、含まれるデータよりもはるかに小さくなっています。しかし、それを bunzip2 で実行すると、最初に書き込んだレコードのセットだけが返されます。残りは破棄されます。

于 2010-09-29T19:17:52.487 に答える