zlibサンプルコード集:すぐに使える圧縮・解凍レシピ

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;

}
“`

コード解説:

  1. ヘッダーファイルのインクルード: zlib.hをインクルードして、zlibの関数を利用できるようにします。
  2. 圧縮対象のデータ: data変数に、圧縮する文字列を格納します。
  3. 圧縮後のバッファ: compressed_dataは、圧縮後のデータを格納するためのバッファです。compressBound()関数は、圧縮後のデータの最大サイズを見積もるために使用されます。これは、圧縮処理を行う前に十分なメモリを確保するために重要です。
  4. 圧縮処理: compress()関数は、実際の圧縮処理を行います。
    • 第1引数: 圧縮後のデータを格納するバッファ。
    • 第2引数: 圧縮後のデータサイズを格納する変数のポインタ。圧縮処理後、実際の圧縮サイズが格納されます。
    • 第3引数: 圧縮対象のデータ。
    • 第4引数: 圧縮対象のデータサイズ。
  5. エラー処理: compress()関数の戻り値がZ_OKでない場合、エラーが発生したことを示します。エラーコードに応じて、適切なエラー処理を行います。
  6. ファイルへの書き込み (オプション): 圧縮されたデータをファイルに書き込む例です。
  7. メモリ解放: 最後に、動的に割り当てられたメモリを解放します。

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;

}
“`

コード解説:

  1. ファイルからの読み込み (オプション): 圧縮されたデータをファイルから読み込む例です。
  2. 解凍後のバッファ: uncompressed_dataは、解凍後のデータを格納するためのバッファです。uncompress()関数を使用する前に、解凍後のデータサイズを事前に知っておく必要があります。この例では、元のデータサイズをハードコードしていますが、実際には、圧縮されたデータに元のサイズを含めるなどの方法で、サイズ情報を伝える必要があります。
  3. 解凍処理: uncompress()関数は、実際の解凍処理を行います。
    • 第1引数: 解凍後のデータを格納するバッファ。
    • 第2引数: 解凍後のデータサイズを格納する変数のポインタ。解凍処理後、実際の解凍サイズが格納されます。
    • 第3引数: 圧縮されたデータ。
    • 第4引数: 圧縮されたデータサイズ。
  4. エラー処理: uncompress()関数の戻り値がZ_OKでない場合、エラーが発生したことを示します。エラーコードに応じて、適切なエラー処理を行います。
  5. メモリ解放: 最後に、動的に割り当てられたメモリを解放します。

重要な注意点:

  • 解凍後のサイズ: 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;

}
“`

コード解説:

  1. z_stream構造体: z_stream構造体は、圧縮・解凍の状態を保持します。zalloc, zfree, opaqueは、メモリ管理のための関数ポインタです。通常はZ_NULLに設定します。
  2. deflateInit()関数: 圧縮処理を初期化します。第1引数にz_stream構造体へのポインタ、第2引数に圧縮レベルを指定します。Z_DEFAULT_COMPRESSIONは、デフォルトの圧縮レベルを使用することを示します。
  3. 入力バッファの設定: strm.avail_inには、入力バッファに残っているデータのサイズを、strm.next_inには、入力バッファへのポインタを設定します。
  4. 圧縮ループ: deflate()関数を繰り返し呼び出して、データを圧縮します。
    • strm.avail_outには、出力バッファの空きサイズを、strm.next_outには、出力バッファへのポインタを設定します。
    • deflate()関数の第2引数には、Z_FINISHを指定することで、最後のデータを処理することを示します。
    • strm.avail_out == 0の間、出力バッファが一杯であるため、ループを継続します。
  5. 後処理: 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;

}
“`

コード解説:

  1. z_stream構造体: 圧縮と同様に、z_stream構造体は、解凍の状態を保持します。
  2. inflateInit()関数: 解凍処理を初期化します。
  3. 入力データの読み込み: fread()関数で、圧縮されたデータをファイルから読み込みます。
  4. 解凍ループ: inflate()関数を繰り返し呼び出して、データを解凍します。
    • inflate()関数の第2引数には、Z_NO_FLUSHを指定することで、利用可能なすべての入力を処理することを示します。
  5. 後処理: 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を使いこなして、データ圧縮の恩恵を最大限に活用してください。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール