首页 > 解决方案 > 解压缩连接的 zlib 流而不读取下一个字节

问题描述

我有一个我无法修改的文件,它由 3 个连接的 zlib 数据组成。数据不是很大(几百千字节)我如何读取它们?有 Qt 函数qUncompress()(已编辑),但它需要一个长度作为参数,我不知道流的实际长度是多少。

解决方案 1:通过流读取数据时,我看到的用于读取数据块的代码并在遇到错误时停止。问题是当“读取固定的数据块”将消耗该块时,如果流的大小 si 不完全是 N 的倍数,则流将被破坏。

伪代码:

while (no error) {
   read N bytes
   decompress_next(these N bytes)
}

... Here there may be up to N-1 totally skipped bytes...

它在 N=1 时有效,但我觉得有点骇人听闻。有更好的选择吗?

解决方案2:解压缩流,再次压缩并获取第一个块的大小。转到偏移量然后读取等...(当输入流不可写时它应该不适用,但它应该在我的情况下工作)

如果代码不平凡,我最终可以使用 ac 或 c++ 库(理想情况下是轻量级的)。

这可能是不可能的,我对 zlib 算法了解不多,如果它知道流何时结束或只是读取“无状态”数据。

编辑:实用程序zlib-flate看起来像是为解决方案#2做的,所以这显然是可能的

标签: c++qtzlib

解决方案


您可以直接使用 zlib 库来完成此操作。这是一些示例代码:

#include <iostream>
#include <zlib.h>
#include <vector>
#include <algorithm>
#include <numeric>
#include <cassert>
#include <string>
#include <array>

std::vector<uint8_t> my_compress(std::string const& in_data)
{
    auto bufsize = compressBound(in_data.size());

    std::vector<uint8_t> outbuf(bufsize);

    auto srcptr = reinterpret_cast<uint8_t const*>(in_data.data());
    size_t destlen = outbuf.size();
    auto result = compress(outbuf.data(), &destlen, srcptr, in_data.size());
    assert(result == Z_OK);

    outbuf.resize(destlen);

    return outbuf;
}

std::vector<uint8_t> generate_concatenated_data()
{
    std::vector<uint8_t> outbuf;
    std::vector<std::string> strings =
    {
        "zlib is a widely-used library",
        "remember to check your error returns",
        "the quick brown fox jumps over the lazy dog",
        "Never tell me the odds."
    };

    for(auto const& s: strings)
    {
        auto compressed = my_compress(s);

        // append each compressed stream to the end of the buffer
        outbuf.insert(end(outbuf), begin(compressed), end(compressed));
    }

    return outbuf;
}

void print_buffer(std::vector<char> const& buf)
{
    std::cout << "buffer contains: ";
    for(char c: buf)
    {
        std::cout << c;
    }
    std::cout << '\n';
}

int main()
{

    std::vector<uint8_t> concat_data = generate_concatenated_data();


    std::array<uint8_t, 1024> scratch = {}; //scratch buffer for decompressing the data. (size doesn't matter )


    z_stream s{};

    // standard zlib init procedure

    s.zalloc = nullptr;
    s.zfree = nullptr;
    s.opaque = nullptr;
    int init_result = inflateInit(&s);
    // insert error checking here.
    assert(init_result == Z_OK);



    s.next_in = concat_data.data();
    s.avail_in = concat_data.size();

    while(s.avail_in > 0)
    {
        // output destination buffer
        std::vector<char> out_data;


        int inflate_ret = 0;
        while(true)
        {

            s.next_out = scratch.data();
            s.avail_out = scratch.size();
            inflate_ret = inflate(&s, Z_NO_FLUSH);

            //make sure we decoded right
            assert(inflate_ret == Z_OK || inflate_ret == Z_STREAM_END);

            auto bytes_decoded = scratch.size() - s.avail_out;


            // there are definitely more efficient ways to append to a vector.
            for(int i = 0; i < bytes_decoded; i++)
            {
                out_data.push_back(scratch[i]);
            }

            // is this stream done?
            if(inflate_ret == Z_STREAM_END) break;

        }
        assert(inflate_ret == Z_STREAM_END);


        // get ready for the next stream
        auto reset_result = inflateReset(&s);
        assert(reset_result == Z_OK);


        // do something with the data.
        print_buffer(out_data);
    }


    //cleanup
    inflateEnd(&s);
    std::cout << "end.\n";

}

您将获得以下输出:

buffer contains: zlib is a widely-used library
buffer contains: remember to check your error returns
buffer contains: the quick brown fox jumps over the lazy dog
buffer contains: Never tell me the odds.
end.

zlib 库的文档可在此处获得:https ://zlib.net/manual.html


推荐阅读