如何对QByteArray进行zlib压缩?

8

我希望在压缩文本时保持与全球其他应用程序(包括Web应用程序)的互操作性。由于qCompress和qUncompress似乎有所不同,因此我正在尝试直接从我的Qt应用程序中使用zlib。

我将接受最简单(最简洁)的答案,该答案向我展示如何直接使用zlib库的QByteArray OR 修改qCompress的输出,以便可以在Qt应用程序之外使用它。

以下是我的尴尬尝试:

QByteArray tdata = QString("Oh noes!").toUtf8();
QByteArray cdata;
uLongf len = 12 + 1.002*tdata.length();
compress(&cdata, &len, &tdata, tdata.length());

错误信息:

错误:无法将“QByteArray *”转换为“Bytef *”,用于参数“1”的“int compress(Bytef *,uLongf *,const Bytef *,uLong)”

之后我尝试使用 QByteArray::constData()

compress(cdata.constData(), &len, &tdata, tdata.length());

但是遇到了以下错误:

错误:从 'const char*' 转换为 'Bytef*' 是无效的

我不知道 Bytef 是什么,所以开始查看 zlib 源代码进行调查。但是我在 QtSources/src/3rdparty/zlib/zconf.h 中只能找到这个。

# define Bytef                 z_Bytef

所以现在我只是迷失了。

你可以使用Boost的iostreams:它具有zlib过滤器。 - akappa
3个回答

9

根据这个在qUncompress中的注释,我认为这很容易。

注意:如果您想使用此函数解压缩使用zlib压缩的外部数据,则需要首先将一个四字节头添加到包含数据的字节数组中。头必须包含预期长度(以字节为单位)的未压缩数据,表示为无符号的大端32位整数。

因此,您可以尝试像这样压缩它:

QByteArray tdata = QString("Oh noes!").toUtf8();
QByteArray compressedData = qCompress(tdata);
compressedData.remove(0, 4);

你说“prepend”,但是你的代码似乎是删除前四个字节而不是在前面插入大小。 - Dusty Campbell
1
代码就是我想要的。qUncompress 需要在前面加上 4 个非标准字节。因此,qCompress 必须在前面加上 4 个非标准字节。如果你使用 qCompress 并移除这些字节,那么你应该只剩下标准的 zlib 内容。 - cgmb
我已经尝试过了。当我使用qUncompress(在预期大小之前添加)时,zlib会抱怨它是损坏的。 - user336063
好的,我的错。我以为你需要在使用zlib数据之前添加前缀。 - Dusty Campbell
@sosukodo:如果解决方案不起作用,为什么你会将这个答案标记为正确答案? - arne
1
因为答案中给出的代码本身就可以正常工作。它证实了获取纯净的zlib压缩QByteArray的最快方法。我遇到的与压缩/解压缩无关的zlib错误问题。 - user336063

1

这是我曾经编写的一些代码,它以指向字节数组的指针、要压缩的字节数和压缩级别作为输入,并使用zlib来压缩输入。结果以字符串形式返回。

 enum compressionLevel
 {
    clFast,
    clSmall,
    clDefault
 };

 const size_t ChunkSize = 262144; //256k default size for chunks fed to zlib

 void compressZlib(const char *s, size_t nbytes, std::string &out, compressionLevel l /*= clDefault*/ )
 {
    int level = Z_DEFAULT_COMPRESSION;
    switch (l)
    {
    case clDefault:
        level = Z_DEFAULT_COMPRESSION; break;
    case clSmall:
        level = Z_BEST_COMPRESSION; break;
    case clFast:
        level = Z_BEST_SPEED; break;
    };

    z_stream strm;
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;
    int ret = deflateInit(&strm, level);
    if (ret != Z_OK)
    {
        throw std::runtime_error("Error while initializing zlib, error code "+ret);
    }
    size_t toCompress = nbytes;
    char *readp = (char*)s;
    size_t writeOffset = out.size();
    out.reserve((size_t)(nbytes * 0.7));
    while (toCompress > 0)
    {
        size_t toRead = std::min(toCompress,ChunkSize);
        int flush = toRead < toCompress ? Z_NO_FLUSH : Z_FINISH;
        strm.avail_in = toRead;
        strm.next_in = (Bytef*)readp;
        char *writep = new char[ChunkSize];
        do{
            strm.avail_out = ChunkSize;
            strm.next_out = (Bytef*)writep;
            deflate(&strm, flush);
            size_t written = ChunkSize - strm.avail_out;
            out.resize(out.size() + written);
            memcpy(&(out[writeOffset]), writep, written);
            writeOffset += written;
        } while (strm.avail_out == 0);
        delete[] writep;
        readp += toRead;
        toCompress -= toRead;
    }
    (void)deflateEnd(&strm);
 }

也许这可以帮助您解决问题,我猜想使用cdata.constData()函数,您可以直接调用此函数。

0

仅为帮助您解决问题的最后一部分:

我不知道Bytef是什么,所以我开始查看zlib源代码进行调查。

有关ByteBytef的定义,请查看zconf.h的332和333行,以及342行:

332    #if !defined(__MACTYPES__)
333    typedef unsigned char  Byte;  /* 8 bits */
...
338    #ifdef SMALL_MEDIUM
339       /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
340    #  define Bytef Byte FAR
341    #else
342       typedef Byte  FAR Bytef;    

FAR 的定义是为了混合模式 MSDOS 编程,否则它不被定义为任何东西(请参见 zconf.h 的 328-330 行)。

因此,在大多数平台上,zlib 中的 typedefs BytefByte 基本上与 unsigned char 相同。因此,您应该能够执行以下操作:

QByteArray tdata = QString("Oh noes!").toUtf8();
QByteArray cdata(compressBound(tdata.length()), '\0');
uLongf len = compressBound(tdata.length());
compress(reinterpret_cast<unsigned char*>(cdata.data()), &len, 
         reinterpret_cast<unsigned char*>(tdata.data()), tdata.length());

该代码无法编译,报错:error: invalid static_cast from type 'char*' to type 'unsigned char*'。 - user336063
好的,我用reinterpret_cast<>()替换了static_cast<>()...这样肯定可以编译。 - Jason

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接