書いた人:西山
Ruby には便利な標準添付ライブラリがたくさんありますが、なかなか知られていないのが現状です。そこで、この連載では Ruby の標準添付ライブラリを紹介していきます。
今回は、zlib について紹介します。
Ruby/zlib は zlib を Ruby から使うための拡張ライブラリです。 gzip ファイルの読み書きもサポートします。
zlib とは、gzip や PNG で使われている可逆圧縮アルゴリズムのライブラリです。 可逆圧縮とは、完全に元に戻せる圧縮です。 主にプログラムやテキストなどの内容が変わると困るものに使われています。
それに対して、完全には元に戻らない圧縮を不可逆圧縮 (非可逆圧縮) といい、画像や音声などで使われています。 zlib は可逆圧縮のみで不可逆圧縮は出来ません。
Zlib::Deflate で圧縮、Zlib::Inflate で元に戻すことが出来ます。 元に戻すことは展開や伸張などとも言います。
クラスメソッドの Zlib::Deflate.deflate と Zlib::Inflate.inflate を使うと、扱えるものが String だけという違いはありますが、Marshal.dump と Marshal.load と同じような感じで、簡単に圧縮したり元に戻したり出来ます。
最初の 11 バイトしかない文字列の圧縮では逆にサイズが増えてしまっています。 これは元の文字列に無駄がほとんどなくて圧縮できないため、zlib が zlib のデータを区別出来るようにつけているヘッダによるオーバーヘッドによるサイズの増加の方が大きくなってしまっているためです。
次に圧縮の効果がわかりやすいように同じ文字列を 100 回繰り返した文字列を圧縮すると、しっかり圧縮できていることがわかります。
gzip コマンドでは、オプションで –fast や –best のように圧縮レベルを指定できます。 Zlib::Deflate.deflate も同じように第 2 引数で圧縮レベルを指定できます。
Zlib::BEST_SPEED は速度優先で圧縮率は低く、Zlib::BEST_COMPRESSION は圧縮率優先で速度は遅くなります。 Zlib::DEFAULT_COMPRESSION は第 2 引数を省略したときと同じで、Zlib::NO_COMPRESSION は圧縮しません。 圧縮しないというのは、既に圧縮されているものなど、さらに圧縮はほとんど期待できないものを Zlib に入れたい時に使うものだと思います。
ここではあらかじめ用意されている定数を使いましたが、0 から 9 の数値を直接指定することも出来ます。
今までの例では、文字列を直接圧縮や展開していたため、クラスメソッドを使っていました。
これらのクラスメソッドでは対象となる文字列が全てメモリ上に存在しないといけないため、巨大なデータを扱う場合にはメモリ消費量が大きくなってしまいます。このような場合には、Zlib::Deflate や Zlib::Inflate のオブジェクトを生成してストリームとして扱うとよいかもしれません。
Zlib::Deflate.new と Zlib::Inflate.new は Zlib::ZStream を継承していて、同じようにストリームとして使えます。
ここでは Zlib::Deflate の例だけ示しておきます。
この使い方の場合、圧縮されたデータ全体は、毎回の Zlib::Deflate#deflate の返値と Zlib::Deflate#finish の返値をつなげたものになります。 Zlib::Deflate#deflate の返値だけや Zlib::Deflate#finish の返値だけでは圧縮されたデータにならないので注意してください。
その他のメソッドの使い方については、Ruby のリファレンスマニュアルや zlib 自体のドキュメントを参照してください。
Zlib::GzipReader で gz ファイルの読み込み、Zlib::GzipWriter で gz ファイルの書き込みが出来ます。
Zlib::GzipReader を使うと gz ファイルを普通のファイルと同じように読み込むことが出来ます。
ここでは、あらかじめ、
などで hoge.gz を用意しておくと次のように File.open と同じような感じで読み込むことが出来ます。 (gzip コマンドのない環境では、次項の Zlib::GzipWriter の例を元にして hoge.gz を作成してください。)
あらかじめ IO オブジェクトがあって、その中に gzip のデータがある場合は、Zlib::GzipReader.wrap で読み込むことが出来ます。 バイナリデータを扱うため、あらかじめ wrap する IO オブジェクトは open のモードに ‘b’ を入れたり binmode を使ったりして、バイナリモードにしておく必要があることに注意してください。
ここでは close 忘れの心配のないブロック付きの方法だけを紹介しましたが、File.open などと同じようにブロックなしで呼び出して、後から close するという使い方も可能です。 読み込み部分も read の他に each や gets など、IO の一般的なメソッドもあります。 詳しくはリファレンスマニュアルを参照してください。
Zlib::GzipWriter を使うと普通のファイルと同じように書き込むことで gz ファイルを作ることが出来ます。
これで
で作成したものとほぼ同じ内容の hoge.gz が出来ます。
Zlib::GzipReader と同じように wrap メソッドもあります。 これも Zlib::GzipReader と同じようにバイナリモードにする必要があることに注意してください。
gzip コマンドと完全に同じ内容のファイルを作成するには、次のようにします。
つまり、gz ファイルには更新日時や元のファイル名が入っていると言うことです。 詳しくはリファレンスマニュアルや zlib のドキュメントを参照してください。
今回は zlib について紹介しました。
ある程度大きなデータをインターネット経由で転送するときなどに使うと便利だと思います。 実際に HTTP で Content-Encoding に使われるなど、zlib の圧縮方式は広く使われています。
西山和広。 Ruby hotlinks 五月雨版や 現在の Ruby リファレンスマニュアルのメンテナをやっています。
Ruby リファレンスマニュアルは現在青木さんによる新システムに移行準備中です。 手伝っていただける方はるりま Wiki を参考にしてお手伝いをお願いします。