2012-01-10 3 views
6

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

Приму простейший (самый минимальный) ответ, который показывает мне, как использовать библиотеку 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()); 

И ошибка:

error: cannot convert 'QByteArray*' to 'Bytef*' for argument '1' to 'int compress(Bytef*, uLongf*, const Bytef*, uLong)'

Затем я попытался с помощью QByteArray :: constData()

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

Но получил следующее сообщение об ошибке:

error: invalid conversion from 'const char*' to 'Bytef*'

Я понятия не имею, что такое Bytef, поэтому я начинаю искать источники zlib для исследования. Но все, что я могу найти для этого в QtSources/SRC/3rdparty/Zlib/zconf.h

# define Bytef     z_Bytef 

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

+0

вы могли бы использовать iostreams BOOST: он имеет ZLIB фильтр – akappa

ответ

6

на основе this ноты в qUncompress, я думаю, что это довольно легко.

Note: If you want to use this function to uncompress external data that was compressed using zlib, you first need to prepend a four byte header to the byte array containing the data. The header must contain the expected length (in bytes) of the uncompressed data, expressed as an unsigned, big-endian, 32-bit integer.

Таким образом, вы, вероятно, может просто сжать его, как это:

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

Вы сказали «PREPEND», но это выглядит как ваш код удаляет первые 4 байта, а не вставляя размер спереди. –

+1

Код - это то, что я имел в виду. qUncompress ожидает добавления 4 нестандартных байтов. Таким образом, qCompress должен иметь 4 нестандартных байта. Если вы используете qCompress и удаляете эти байты, вы должны иметь только стандартный материал zlib. – cgmb

+0

Я уже пробовал это. Когда я использую qUncompress (добавив ожидаемый размер), zlib жалуется, что он поврежден. –

0

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

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

I have no idea what a Bytef is so I start looking in the zlib sources to investigate.

Для определения 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

Этот код не может скомпилировать сообщение: error: invalid static_cast from type 'char *' для ввода 'unsigned char *' –

+0

Хорошо, я заменил 'static_cast <>()' на 'reinterpret_cast <>()' ... которые должны обязательно скомпилировать. – Jason

Смежные вопросы