2012-01-10 21 views
6

Desidero mantenere l'interoperabilità tra tutte le altre applicazioni sul pianeta (incluse le applicazioni Web) durante la compressione del testo. Dal momento che qCompress e qUncompress sembrano andare controcorrente, sto provando a usare zlib direttamente dalla mia applicazione Qt.Come comprimere zlib un QByteArray?

Accetterò la risposta più semplice (la maggior parte minima) che mi mostra come utilizzare la libreria zlib con un QByteArray direttamente O modificare l'uscita del qCompress in modo che possa essere utilizzato al di fuori di un'applicazione Qt.

Ecco il mio tentativo imbarazzante:

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

E l'errore:

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

Poi ho provato ad utilizzare QByteArray :: constData()

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

ma ho ottenuto il seguente errore:

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

Non ho idea di cosa sia un Bytef quindi comincio a cercare nelle fonti zlib per indagare. Ma tutto quello che posso trovare per questo è in QtSources/src/3rdparty/zlib/zconf.h

# define Bytef     z_Bytef 

Così ora sto appena perso.

+0

si potrebbe usare iostreams di Boost: ha un filtro di zlib – akappa

risposta

6

Sulla base this nota qUncompress, penso che sia abbastanza facile.

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.

Così si può probabilmente solo comprimere in questo modo:

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

Hai detto "prepend", ma sembra che il codice rimuove i primi 4 byte invece di inserire il taglia in avanti –

+1

Il codice è ciò che intendevo che fosse. qUncompress si aspetta 4 byte non standard anteposto. Pertanto, qCompress deve aver anteposto 4 byte non standard. Se si utilizza qCompress e si rimuovono quei byte, si dovrebbe avere solo il materiale standard di zlib. – cgmb

+0

L'ho già provato. Quando uso qUncompress (prepagando la dimensione prevista), zlib si lamenta che è corrotto. –

0

Ecco un codice che ho scritto una volta che ottiene come input un puntatore a un array di byte, il numero di byte da comprimere e il livello di compressione e quindi usa zlib per comprimere l'input. Il risultato viene restituito in una stringa.

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); 
} 

Forse questo ti aiuta a risolvere il problema, credo che con il cdata.constData() si può direttamente chiamare questa funzione

0

Giusto per aiutarvi con l'ultima sezione della tua domanda qui:

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

Per le definizioni di Byte e Bytef, guarda le righe 332 e 333 di zconf.h, così come la riga 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;  

La definizione di FAR è per la programmazione MSDOS in modalità mista, altrimenti non è definita come nulla (vedere le righe 328-330 di zconf.h).

Così i tipi di zlib Bytef e Byte sono fondamentalmente gli stessi di unsigned char sulla maggior parte delle piattaforme.Pertanto si dovrebbe essere in grado di-fare quanto segue:

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

Questo codice non riesce a compilare dichiarando: errore: statico_cast non valido da tipo 'char *' per digitare 'unsigned char *' –

+0

Ok, ho sostituito il 'static_cast <>()' con un 'reinterpret_cast <>()' ... che dovrebbe assolutamente compilare. – Jason