Как zlib сжать QByteArray? - PullRequest
6 голосов
/ 11 января 2012

Я бы хотел поддерживать совместимость между всеми другими приложениями на планете (включая веб-приложения) при сжатии текста.Так как qCompress и qUncompress, кажется, идут вразрез, я пытаюсь использовать zlib непосредственно из моего приложения Qt.

Я приму самый простой (самый минимальный) ответ, который показывает мне, как использовать библиотеку zlib снепосредственно QByteArray ИЛИ изменить вывод 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

Так что теперь я просто потерян.

Ответы [ 3 ]

7 голосов
/ 11 января 2012

Основываясь на этой заметке в qUncompress, я думаю, что это довольно просто.

Примечание. Если вы хотите использовать эту функцию для распаковки внешних данных, сжатых с помощью zlib, сначала необходимо добавить четырехбайтовый заголовок к байтовому массиву, содержащему данные. Заголовок должен содержать ожидаемую длину (в байтах) несжатых данных, выраженную как 32-разрядное целое число без знака с прямым порядком байтов.

Так что вы можете просто сжать его так:

QByteArray tdata = QString("Oh noes!").toUtf8();
QByteArray compressedData = qCompress(tdata);
compressedData.remove(0, 4);
0 голосов
/ 11 января 2012

Просто чтобы помочь вам с последним разделом вашего вопроса здесь:

Я понятия не имею, что такое Bytef, поэтому я начинаю искать в источниках zlib для исследования.

Для определения Byte и Bytef посмотрите строки 332 и 333 zconf.h, а также строку 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, в противном случае оно не определено как что-либо (см. строки 328-330 в zconf.h).

Таким образом, zlib typedefs Bytef и Byte в основном совпадают с 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());
0 голосов
/ 11 января 2012

Вот некоторый код, который я однажды написал, который получает в качестве входных данных указатель на байтовый массив, количество байтов для сжатия и уровень сжатия, а затем использует 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 () вы можете напрямую вызвать эту функцию

...