74080


gz 圧縮されたバイナリデータを char の配列で持った状態でそれを解凍したいです

以下の様なテストコードをかいてみたんですが  
uncompress の結果が -3  
(<https://zlib.net/manual.html#Basic> によると Z_DATA_ERROR)  
になってしまいます

どこが問題なんでしょうか

[code]

    #include <iostream>
    #include <zlib.h>
    
    int main() {
      char data[] = {
        0x1f,0x8b,0x08,0x08,0x59,0x2e,0x2d,0x60,0x00,0x03,
        0x74,0x65,0x73,0x74,0x2e,0x6a,0x73,0x6f,0x6e,0x00,
        0xab,0x56,0xaa,0x50,0xb2,0x32,0xd4,0x51,0xaa,0x54,
        0xb2,0x32,0xaa,0x05,0x00,0x5e,0x92,0xca,0x0d,0x0d,
        0x00,0x00,0x00
      };
      char *out = (char *)malloc(100);
      unsigned long out_size = 100;
      int result = uncompress((unsigned char *)out, &out_size, (unsigned char *)data, 43);
      printf("%d\n", result);
      std::cout << out << std::endl;
      return 0;
    }
    
[/code]

data 配列は test.json

[code]

    {"x":1,"y":2}
    
[/code]

というファイルを gzip コマンドで圧縮した test.json.gz をバイナリファイルで開いたもの  
2文字ずつ 0xとカンマをつけて置換したものなので書写しとかはないはずでサイズも４３バイトで一致してます  
gzip -d test.json.gz で当然元に戻ります


CC BY-SA 4.0
2021-02-17T15:24:50.250
74075
2021-02-18T03:18:58.247
2021-02-18T03:05:15.227
question
c++
gzip で圧縮したテキストに zlib uncompress を行うとデータエラーになる


同じ`zlib`のマニュアルの先にgzip用関数の説明があって、gzip形式のファイルにアクセスするには`gz`で始まる関数を使う必要があるようです。  
[zlib 1.2.11 Manual](https://zlib.net/manual.html)  
[gzip File Access Functions](https://zlib.net/manual.html#Gzip)

こちらに対応版数は1.2.6のようですが日本語で説明しているページがあります。  
[zlib の使い方](https://www.s-yata.jp/docs/zlib/)  
後半の「gzip 形式のファイル」以後を参照してください。

> **ファイル操作の概要**  
>  zlib には gzip 形式のファイルを操作するための型（gzFile）と関数群（gz*()）が用意されています．gzFile は FILE *
> の代替になり，gz*() は fopen() や fclose()
> などの代替になります．ただし，一部の関数については，インタフェースが異なっていたり，機能に制限があったりします．

その記事の最後にサンプルコードを用意したと書かれているので、参考になるかもしれません。

> zlib 形式および gzip 形式の圧縮・伸長をおこなうプログラムのサンプルコードとして zlib-test.c を用意しました．C99
> の機能を使っているので，gcc には -std=c99 を渡すようにしてください．  
>  コマンドライン引数による圧縮形式や圧縮レベルの切り替えが可能になっています．-h もしくは --help をオプションとして渡すことにより，zlib
> のバージョンとコマンドライン引数の一覧を確認することができます．

[zlib-test.c](https://www.s-yata.jp/docs/zlib/zlib-test.c)

他に上記記事の中に「zlib を使って gzip 形式のファイルを読み書きする方法が説明されています．」という紹介リンクがあり、以下になります。  
[zlibを使ってみる](https://www.fireproject.jp/feature/c-language/etc/zlib.html)

* * *

@metropolis さんの回答と同様のことが、この記事の回答に書いてありました。  
[How to decompress gzipstream with
zlib](https://stackoverflow.com/q/14688296/9014308)  
[該当の回答](https://stackoverflow.com/a/46106007/9014308)

結果がテキストの場合だけ動作すると書いてありますが、その辺は質問記事のソースもそうですが、`char`と`unsigned
char`を混在させているからかも？  
全部`unsigned char`で統一してみると何か変わるかもしれませんね。


CC BY-SA 4.0
2021-02-17T16:40:09.863
74077
2021-02-18T03:18:58.247
2021-02-18T03:18:58.247
26370
26370
74075
answer


`zlib.h` には以下の様に記載されています。

> The compressed data format used by default by the in-memory functions is the
> zlib format, which is a zlib wrapper documented in RFC 1950, wrapped around
> a deflate stream, which is itself documented in RFC 1951.

なので、`uncompress()` 関数を使うのでしたら、データは ZLIB Compressed Data Format で渡す必要があります。

文字列 `{"x":1,"y":2}` を ZLIB Compressed Data Format に変換(圧縮)します。

[code]

    $ printf '{"x":1,"y":2}' | zlib-flate -compress | xxd --include
      0x78, 0x9c, 0xab, 0x56, 0xaa, 0x50, 0xb2, 0x32, 0xd4, 0x51, 0xaa, 0x54,
      0xb2, 0x32, 0xaa, 0x05, 0x00, 0x18, 0x66, 0x03, 0x75
    
[/code]

`data[]` の内容(配列の要素)を入れ替えます。

[code]

      unsigned char data[] = {
        0x78, 0x9c, 0xab, 0x56, 0xaa, 0x50, 0xb2, 0x32, 0xd4, 0x51, 0xaa, 0x54,
        0xb2, 0x32, 0xaa, 0x05, 0x00, 0x18, 0x66, 0x03, 0x75
      };
    
[/code]

コンパイルして実行します。

[code]

    $ g++ -Wall -Wextra -g zlib_uncompress.cc -o zlib_uncompress -lz && ./zlib_uncompress
    0
    {"x":1,"y":2}
    
[/code]

GZIP file format(RFC 1952) の場合は `z_stream` を使います。

**zlib_deflate_gzip.cc**

[code]

    #include <iostream>
    #include <vector>
    #include <zlib.h>
    
    int main() {
      std::vector<Bytef> data = {
        0x1f, 0x8b, 0x08, 0x08, 0x59, 0x2e, 0x2d, 0x60, 0x00, 0x03,
        0x74, 0x65, 0x73, 0x74, 0x2e, 0x6a, 0x73, 0x6f, 0x6e, 0x00,
        0xab, 0x56, 0xaa, 0x50, 0xb2, 0x32, 0xd4, 0x51, 0xaa, 0x54,
        0xb2, 0x32, 0xaa, 0x05, 0x00, 0x5e, 0x92, 0xca, 0x0d, 0x0d,
        0x00, 0x00, 0x00
      };
      std::vector<Bytef> out(100);
    
      z_stream ds = {};
      ds.zalloc = Z_NULL;
      ds.zfree = Z_NULL;
      ds.opaque = Z_NULL;
      ds.next_in  = data.data();
      ds.avail_in = data.size();
      ds.next_out = out.data();
      ds.avail_out = out.size();
    
      if (inflateInit2(&ds, MAX_WBITS + 16) != Z_OK)
        return -1;
    
      inflate(&ds, Z_FINISH);
      if (inflateEnd(&ds) != Z_OK)
        return -1;  
    
      std::cout << out.data() << std::endl;
    
      return 0;  
    }
    
[/code]

コンパイルして実行します。

[code]

    $ g++ -Wall -Wextra -g zlib_deflate_gzip.cc -o zlib_deflate_gzip -lz && ./zlib_deflate_gzip
    {"x":1,"y":2}
    
[/code]

※ 文字列 `{"x":1,"y":2}` は 13 bytes という短さなので `inflate(&ds, Z_FINISH);` で済ませています


CC BY-SA 4.0
2021-02-17T19:11:59.010
74080
2021-02-17T19:11:59.010
74075
answer