zlibサンプルコード集:すぐに使える圧縮・解凍レシピ
zlibは、ロスレスデータ圧縮のための、高速かつ自由に使用できるライブラリです。その移植性の高さから、gzip、png、httpなどの様々なアプリケーションやフォーマットで使用されています。本記事では、zlibライブラリを利用して、データを圧縮・解凍するためのサンプルコードを様々なシナリオに沿って紹介します。初心者から中級者までが、zlibを効果的に利用できるよう、詳細な説明と注意点を盛り込みました。
1. はじめに:zlibとは何か、なぜ使うのか
zlibは、Jean-loup GaillyとMark Adlerによって開発された、広く利用されているデータ圧縮ライブラリです。gzipファイルフォーマットのコア部分でもあり、gzip形式での圧縮・解凍機能を提供します。主な特徴は以下の通りです。
- ロスレス圧縮: 圧縮されたデータを完全に元の状態に戻せるため、データの損失を許容できない場合に最適です。
- 高速な処理速度: 様々なプラットフォーム向けに最適化されており、効率的な圧縮・解凍を実現します。
- クロスプラットフォーム対応: Windows、macOS、Linuxなど、様々なOSで動作します。
- オープンソース: 無償で利用でき、商用利用も可能です。
- 簡単なAPI: 比較的シンプルなAPIで、容易にプログラムに組み込むことができます。
zlibを使用する主な理由は以下の通りです。
- ストレージ容量の削減: データサイズを小さくすることで、必要なストレージ容量を削減できます。
- ネットワーク帯域幅の節約: データ転送量を減らすことで、ネットワークの負荷を軽減し、転送時間を短縮できます。
- パフォーマンスの向上: 圧縮されたデータを扱うことで、ディスクI/Oやメモリ使用量を削減し、アプリケーションのパフォーマンスを向上させることができます。
2. zlibのインストールと環境構築
zlibを使用する前に、開発環境にzlibライブラリをインストールする必要があります。
- Linux (Debian/Ubuntu):
bash
sudo apt-get update
sudo apt-get install zlib1g-dev - Linux (Fedora/CentOS):
bash
sudo yum install zlib-devel - macOS (Homebrew):
bash
brew install zlib - Windows:
- MinGW: MinGWのインストール時に、zlibを選択してインストールします。
- Visual Studio: vcpkgなどのパッケージマネージャーを使用してインストールします。 例えば、
vcpkg install zlib
コマンドを実行します。
コンパイル時には、zlibライブラリをリンクする必要があります。例えば、gccを使用する場合、-lz
オプションを付けます。
bash
gcc your_program.c -lz -o your_program
3. 基本的な圧縮・解凍のサンプルコード (C言語)
zlibライブラリを使用する上で基本となる、メモリ上のデータを圧縮・解凍するC言語のサンプルコードを紹介します。
3.1 圧縮 (compress):
“`c
include
include
include
include
define CHUNK 16384 // 処理するデータのチャンクサイズ
int main() {
// 圧縮対象のデータ
const char *data = “This is a sample string to be compressed using zlib. This is a sample string to be compressed using zlib. This is a sample string to be compressed using zlib.”;
size_t data_len = strlen(data);
// 圧縮後のデータを格納するバッファ
Bytef *compressed_data = NULL;
uLong compressed_len;
// 圧縮処理
compressed_len = compressBound(data_len); // 圧縮後の最大サイズを見積もる
compressed_data = (Bytef*)malloc(compressed_len);
if (compressed_data == NULL) {
fprintf(stderr, "Memory allocation failed.\n");
return 1;
}
int ret = compress(compressed_data, &compressed_len, (const Bytef*)data, data_len);
if (ret != Z_OK) {
fprintf(stderr, "Compression failed with error code: %d\n", ret);
free(compressed_data);
return 1;
}
// 圧縮結果を表示
printf("Original size: %zu bytes\n", data_len);
printf("Compressed size: %lu bytes\n", compressed_len);
// 圧縮されたデータをファイルに書き込む (オプション)
FILE *fp = fopen("compressed.zlib", "wb");
if (fp == NULL) {
perror("Error opening file for writing");
free(compressed_data);
return 1;
}
fwrite(compressed_data, 1, compressed_len, fp);
fclose(fp);
// メモリ解放
free(compressed_data);
return 0;
}
“`
コード解説:
- ヘッダーファイルのインクルード:
zlib.h
をインクルードして、zlibの関数を利用できるようにします。 - 圧縮対象のデータ:
data
変数に、圧縮する文字列を格納します。 - 圧縮後のバッファ:
compressed_data
は、圧縮後のデータを格納するためのバッファです。compressBound()
関数は、圧縮後のデータの最大サイズを見積もるために使用されます。これは、圧縮処理を行う前に十分なメモリを確保するために重要です。 - 圧縮処理:
compress()
関数は、実際の圧縮処理を行います。- 第1引数: 圧縮後のデータを格納するバッファ。
- 第2引数: 圧縮後のデータサイズを格納する変数のポインタ。圧縮処理後、実際の圧縮サイズが格納されます。
- 第3引数: 圧縮対象のデータ。
- 第4引数: 圧縮対象のデータサイズ。
- エラー処理:
compress()
関数の戻り値がZ_OK
でない場合、エラーが発生したことを示します。エラーコードに応じて、適切なエラー処理を行います。 - ファイルへの書き込み (オプション): 圧縮されたデータをファイルに書き込む例です。
- メモリ解放: 最後に、動的に割り当てられたメモリを解放します。
3.2 解凍 (uncompress):
“`c
include
include
include
include
define CHUNK 16384
int main() {
// 圧縮されたデータ (上記の圧縮例で生成されたもの)
Bytef *compressed_data = NULL;
uLong compressed_len;
// 解凍後のデータを格納するバッファ
Bytef *uncompressed_data = NULL;
uLong uncompressed_len;
// 圧縮されたデータをファイルから読み込む (オプション)
FILE *fp = fopen("compressed.zlib", "rb");
if (fp == NULL) {
perror("Error opening file for reading");
return 1;
}
fseek(fp, 0, SEEK_END);
compressed_len = ftell(fp);
fseek(fp, 0, SEEK_SET);
compressed_data = (Bytef*)malloc(compressed_len);
if (compressed_data == NULL) {
perror("Memory allocation failed");
fclose(fp);
return 1;
}
fread(compressed_data, 1, compressed_len, fp);
fclose(fp);
// 解凍後のサイズを事前に知っておく必要があります。
// この例では、元のデータサイズをハードコードしていますが、
// 実際には、圧縮されたデータに元のサイズを含めるなどの方法で
// サイズ情報を伝える必要があります。
uncompressed_len = 192; // 元の文字列の長さ
uncompressed_data = (Bytef*)malloc(uncompressed_len);
if (uncompressed_data == NULL) {
fprintf(stderr, "Memory allocation failed.\n");
free(compressed_data);
return 1;
}
int ret = uncompress(uncompressed_data, &uncompressed_len, compressed_data, compressed_len);
if (ret != Z_OK) {
fprintf(stderr, "Decompression failed with error code: %d\n", ret);
free(compressed_data);
free(uncompressed_data);
return 1;
}
// 解凍結果を表示
printf("Original size: %lu bytes\n", uncompressed_len);
printf("Uncompressed data: %s\n", uncompressed_data);
// メモリ解放
free(compressed_data);
free(uncompressed_data);
return 0;
}
“`
コード解説:
- ファイルからの読み込み (オプション): 圧縮されたデータをファイルから読み込む例です。
- 解凍後のバッファ:
uncompressed_data
は、解凍後のデータを格納するためのバッファです。uncompress()
関数を使用する前に、解凍後のデータサイズを事前に知っておく必要があります。この例では、元のデータサイズをハードコードしていますが、実際には、圧縮されたデータに元のサイズを含めるなどの方法で、サイズ情報を伝える必要があります。 - 解凍処理:
uncompress()
関数は、実際の解凍処理を行います。- 第1引数: 解凍後のデータを格納するバッファ。
- 第2引数: 解凍後のデータサイズを格納する変数のポインタ。解凍処理後、実際の解凍サイズが格納されます。
- 第3引数: 圧縮されたデータ。
- 第4引数: 圧縮されたデータサイズ。
- エラー処理:
uncompress()
関数の戻り値がZ_OK
でない場合、エラーが発生したことを示します。エラーコードに応じて、適切なエラー処理を行います。 - メモリ解放: 最後に、動的に割り当てられたメモリを解放します。
重要な注意点:
- 解凍後のサイズ:
uncompress()
関数を使用する前に、解凍後のデータサイズを事前に知っておく必要があります。これは、zlibの制限事項です。通常、圧縮されたデータに元のデータサイズを含めるなどの方法で、サイズ情報を伝えます。 - メモリ管理: 圧縮・解凍処理では、動的にメモリを割り当てるため、メモリリークが発生しないように、必ず
free()
関数でメモリを解放してください。 - エラー処理: 圧縮・解凍処理は、様々な理由で失敗する可能性があります。必ずエラー処理を行い、異常終了を防いでください。
4. ストリーム圧縮・解凍のサンプルコード (C言語)
大規模なデータを扱う場合、メモリに一度にロードせずに、ストリームとして圧縮・解凍する方が効率的です。zlibは、ストリーム圧縮・解凍のための関数を提供しています。
4.1 ストリーム圧縮 (deflate):
“`c
include
include
include
include
define CHUNK 16384
int main() {
// 圧縮対象のデータ
const char *data = “This is a sample string to be compressed using zlib. This is a sample string to be compressed using zlib. This is a sample string to be compressed using zlib.”;
size_t data_len = strlen(data);
// 圧縮後のデータを格納するバッファ
Bytef out[CHUNK];
// z_stream構造体の初期化
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
// deflateInit()関数の呼び出し
int ret = deflateInit(&strm, Z_DEFAULT_COMPRESSION);
if (ret != Z_OK) {
fprintf(stderr, "deflateInit failed with error code: %d\n", ret);
return 1;
}
// 入力バッファの設定
strm.avail_in = data_len;
strm.next_in = (Bytef*)data;
// ファイルへの書き込み
FILE *fp = fopen("compressed_stream.zlib", "wb");
if (fp == NULL) {
perror("Error opening file for writing");
deflateEnd(&strm);
return 1;
}
// 圧縮ループ
do {
strm.avail_out = CHUNK;
strm.next_out = out;
// deflate()関数の呼び出し
ret = deflate(&strm, Z_FINISH); // Z_FINISHは、最後のデータを処理することを示す
if (ret == Z_STREAM_ERROR) {
fprintf(stderr, "deflate failed with error code: %d\n", ret);
deflateEnd(&strm);
fclose(fp);
return 1;
}
// 出力データの書き込み
size_t have = CHUNK - strm.avail_out;
fwrite(out, 1, have, fp);
} while (strm.avail_out == 0); // 出力バッファが一杯の間、ループ
// 後処理
deflateEnd(&strm);
fclose(fp);
printf("Stream compression completed.\n");
return 0;
}
“`
コード解説:
z_stream
構造体:z_stream
構造体は、圧縮・解凍の状態を保持します。zalloc
,zfree
,opaque
は、メモリ管理のための関数ポインタです。通常はZ_NULL
に設定します。deflateInit()
関数: 圧縮処理を初期化します。第1引数にz_stream
構造体へのポインタ、第2引数に圧縮レベルを指定します。Z_DEFAULT_COMPRESSION
は、デフォルトの圧縮レベルを使用することを示します。- 入力バッファの設定:
strm.avail_in
には、入力バッファに残っているデータのサイズを、strm.next_in
には、入力バッファへのポインタを設定します。 - 圧縮ループ:
deflate()
関数を繰り返し呼び出して、データを圧縮します。strm.avail_out
には、出力バッファの空きサイズを、strm.next_out
には、出力バッファへのポインタを設定します。deflate()
関数の第2引数には、Z_FINISH
を指定することで、最後のデータを処理することを示します。strm.avail_out == 0
の間、出力バッファが一杯であるため、ループを継続します。
- 後処理:
deflateEnd()
関数を呼び出して、圧縮処理を終了します。
4.2 ストリーム解凍 (inflate):
“`c
include
include
include
include
define CHUNK 16384
int main() {
// 圧縮されたデータを含むファイル
FILE *fp = fopen(“compressed_stream.zlib”, “rb”);
if (fp == NULL) {
perror(“Error opening file for reading”);
return 1;
}
// 解凍後のデータを格納するバッファ
Bytef out[CHUNK];
// z_stream構造体の初期化
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = 0;
strm.next_in = Z_NULL;
// inflateInit()関数の呼び出し
int ret = inflateInit(&strm);
if (ret != Z_OK) {
fprintf(stderr, "inflateInit failed with error code: %d\n", ret);
fclose(fp);
return 1;
}
// 出力ファイル
FILE *outfile = fopen("uncompressed_stream.txt", "wb");
if (outfile == NULL) {
perror("Error opening file for writing");
inflateEnd(&strm);
fclose(fp);
return 1;
}
// 解凍ループ
Bytef in[CHUNK];
do {
// 入力データの読み込み
strm.avail_in = fread(in, 1, CHUNK, fp);
if (ferror(fp)) {
perror("Error reading file");
inflateEnd(&strm);
fclose(fp);
fclose(outfile);
return 1;
}
if (strm.avail_in == 0) break; // ファイルの終端
strm.next_in = in;
// 解凍処理
do {
strm.avail_out = CHUNK;
strm.next_out = out;
ret = inflate(&strm, Z_NO_FLUSH); // Z_NO_FLUSHは、利用可能なすべての入力を処理することを示す
if (ret == Z_STREAM_ERROR || ret == Z_DATA_ERROR) {
fprintf(stderr, "inflate failed with error code: %d\n", ret);
inflateEnd(&strm);
fclose(fp);
fclose(outfile);
return 1;
}
size_t have = CHUNK - strm.avail_out;
fwrite(out, 1, have, outfile);
} while (strm.avail_out == 0); // 出力バッファが一杯の間、ループ
} while (ret != Z_STREAM_END); // ストリームの終端に達するまでループ
// 後処理
inflateEnd(&strm);
fclose(fp);
fclose(outfile);
printf("Stream decompression completed.\n");
return 0;
}
“`
コード解説:
z_stream
構造体: 圧縮と同様に、z_stream
構造体は、解凍の状態を保持します。inflateInit()
関数: 解凍処理を初期化します。- 入力データの読み込み:
fread()
関数で、圧縮されたデータをファイルから読み込みます。 - 解凍ループ:
inflate()
関数を繰り返し呼び出して、データを解凍します。inflate()
関数の第2引数には、Z_NO_FLUSH
を指定することで、利用可能なすべての入力を処理することを示します。
- 後処理:
inflateEnd()
関数を呼び出して、解凍処理を終了します。
5. メモリ圧縮レベルの調整
deflateInit()
関数の第2引数で、圧縮レベルを指定できます。圧縮レベルは0から9までの整数で指定し、0は無圧縮、9は最高圧縮率を示します。Z_DEFAULT_COMPRESSION
は、デフォルトの圧縮レベルを使用することを意味します。
c
int ret = deflateInit(&strm, Z_BEST_COMPRESSION); // 最高圧縮率
int ret = deflateInit(&strm, Z_BEST_SPEED); // 最高速度
int ret = deflateInit(&strm, 1); // 圧縮レベル1
圧縮レベルを高くすると、圧縮率は向上しますが、処理時間も長くなります。逆に、圧縮レベルを低くすると、処理時間は短くなりますが、圧縮率は低下します。適切な圧縮レベルは、データの特性や、求められるパフォーマンスによって異なります。
6. エラー処理の重要性
zlibの関数は、様々な理由でエラーを返す可能性があります。例えば、メモリ割り当ての失敗、無効な入力データ、圧縮されたデータの破損などが考えられます。エラーが発生した場合、適切なエラー処理を行わないと、プログラムが異常終了する可能性があります。
zlibの関数は、エラーコードを返します。エラーコードは、zlib.h
で定義されています。代表的なエラーコードは以下の通りです。
Z_OK
: 正常終了Z_MEM_ERROR
: メモリ割り当てエラーZ_STREAM_ERROR
: ストリームのエラーZ_DATA_ERROR
: データのエラーZ_BUF_ERROR
: バッファのエラー
エラーが発生した場合、エラーコードに応じて、適切なエラーメッセージを出力し、必要なリソースを解放してから、プログラムを終了するようにしてください。
7. まとめ
本記事では、zlibライブラリを使用して、データを圧縮・解凍するための様々なサンプルコードを紹介しました。基本的な圧縮・解凍から、ストリーム圧縮・解凍、圧縮レベルの調整まで、zlibを効果的に利用するための知識を習得できたはずです。
zlibは、様々なアプリケーションで使用されている、非常に強力なライブラリです。本記事で紹介したサンプルコードを参考に、zlibを使いこなして、データ圧縮の恩恵を最大限に活用してください。