質問

データベースをクエリし、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のファイルがあり、実行されています bzip2 その結果、217,275のサイズがあります。言い換えれば、非圧縮ファイルは、PythonのBZIPコーデックを使用して圧縮されたファイルよりも大幅に小さくなっています。 実行以外にこれを修正する方法はありますか bzip2 コマンドラインで?

PythonのGZIPコーデックを試しました(ラインを変更します codecs.open(archive_file, 'a+', 'zip'))問題を修正したかどうかを確認します。私はまだ大きなファイルを取得しますが、私も取得します gzip: archive_file: not in gzip format ファイルを非圧縮しようとしたときのエラー。 そこで何が起こっているのですか?


編集: :私はもともと、書き込みモードではなく、付録モードでファイルを開きました。これは問題であるかもしれないし、そうでないかもしれませんが、ファイルが「W」モードで開かれているかどうかは依然として問題になります。

役に立ちましたか?

解決

他のポスターが指摘しているように、問題は codecs ライブラリは、データをエンコードするための増分エンコーダーを使用しません。代わりに、供給されたデータのすべてのスニペットをエンコードします write 圧縮ブロックとしての方法。これは恐ろしく非効率的であり、ストリームで動作するように設計されたライブラリにとってひどいデザインの決定です。

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

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に戻る圧縮ファイルを読み取る必要がある場合は、ファイルごとに単一のストリームに固執します。

他のヒント

問題は、出力がすべてに書かれているということです write(). 。これにより、各ラインが独自のBZIPブロックで圧縮されます。

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

問題は、付録モードの使用によるものであり、その結果、複数の圧縮されたデータのブロックが含まれているファイルが得られます。この例を見てください:

>>> 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が単一のジップストリームを期待していることです。複数の連結されたストリームを使用して公式の動作が何であるかを確認するために仕様を確認する必要がありますが、私の経験では、最初のストリームを処理し、残りのデータを無視します。それがPythonがしていることです。

Bunzip2が同じことをしていることを期待しています。そのため、実際にはファイルが圧縮されており、含まれるデータよりもはるかに小さいです。しかし、Bunzip2を介して実行すると、最初に書いたレコードのセットだけが戻ってきます。残りは廃棄されます。

これがコーデックのやり方とどれほど違いがあるかはわかりませんが、GZIPモジュールからgzipfileを使用すると、ファイルに段階的に追加できますが、大量のデータを書いていない限り、それほどうまく圧縮されません。時間(たぶん> 1 kb)。これは、圧縮アルゴリズムの性質です。書いているデータが非常に重要ではない場合(つまり、プロセスが死んだ場合、それを失うことに対処できます)、より大きなデータのチャンクを書き出すインポートクラスをラッピングするバッファーGZIPFILEクラスを書くことができます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top