Multiple BZ2ファイルの展開  

正式名称かどうか分かりませんが、multiple bz2というものがあります。簡単に言うと複数のBZ2が合わさったもので、私が知っている限りではbzip2のマルチスレッド対応版であるpbzip2で生成されます。
このpbzip2はマルチスレッドで処理するために圧縮、展開がかなり速くなるのですが、現在のPythonのbz2.BZ2Fileではそのままでは展開できず、一部のみの展開になってしまいます(展開後ファイルサイズ900k以上のもの)。もちろんpbunzip2またはbunzip2を使えば何の問題もないのですが、bz2モジュールでの展開を試みてみたのでメモします。なお、遠くない将来にはPythonでも対応しそうです(たぶん)。

上記のバグトラックではPython 3用のパッチは提供されていますが、2.x用はないようです。

We originally wrote it against 2.5, but I've updated the patch to py3k trunk, and attached it here. If there's interest in a patch against 2.7 trunk, please let me know.

と書いてあるので、将来的には2.xにも提供されるかも。2.5用のを公開してくれていれば助かったのですが。
2年ぐらい前のトラックなので現在どうなっているか分かりませんが・・・

手順  

いろいろと書きましたが、大して難しくないです。

Multiple bz2  

ここでいうMultiple bz2とは以下のような形式です。

|BZ2ヘッダ-内容|BZ2ヘッダ-内容|BZ2ヘッダ-内容...

というようにBZ2のパートがつながっているだけのものです。pbzip2ディフォルトでは900kbytes(まさに900000bytes)ごとに分けて圧縮されるようです。

展開の実装  

bz2モジュールのBZ2Fileではなく、BZ2Decompressorを使います。BZ2Decompressorオブジェクトを生成し、decompressメソッドに内容を渡すと、展開されなかった残りの部分(次のBZ2パート以降)がunused_dataにセットされるのでそれをさらにdecompressし・・・という事を繰り返します。

#!/usr/bin/python
# -*- coding: utf-8 -*-
import bz2
fh = open("infile.bz2", "rb")  # 読み込みファイル
res = ""
buf = fh.read()
fh.close()
while len(buf) > 0:
    dec = bz2.BZ2Decompressor()
    res += dec.decompress(buf)
    buf = dec.unused_data
out = open("outfile", "wb")    # ファイルに書き出す
out.write(res)
out.close()

まあ、pbunzip2が使用できるならその方が圧倒的に速いのですが、bunzip2とはほぼ同じ速度なので外部プログラムに頼りたくない時はいいかもしれません。

ちょっと凝ったことをすればマルチスレッドでも可能だと思いますが、GILのためCPUパワーを有効活用ができないのでシングルスレッド処理で実装しています。しっかりマルチコアで処理をするにはCでモジュールを書かないとダメかも(そこまでするならpbunzip2を使うかな・・・)。

コメント