2012-01-10 15 views
6

Chciałbym zachować zgodność operacyjną wszystkich innych aplikacji na świecie (w tym aplikacji internetowych) podczas kompresji tekstu. Ponieważ qCompress i qUncompress wydają się działać przeciwko ziarnu, próbuję użyć zlib bezpośrednio z mojej aplikacji Qt.Jak zlib skompresować QByteArray?

zgodzę najprostszy (minimalny) najbardziej odpowiedź, że pokazuje mi, jak korzystać z biblioteki zlib z QByteArray bezpośrednio LUB zmodyfikować wyjście qCompress tak, że może on być stosowany poza aplikacją Qt.

Oto mój kłopotliwy próba:

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

i błąd:

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

Potem próbowałem za pomocą QByteArray :: constData()

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

Ale uzyskałem następujący błąd:

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

Nie mam pojęcia, czym jest Bytef, więc szukam źródeł zlib, aby zbadać. Ale wszystko, co mogę znaleźć na to w QtSources/src/3rdParty/zlib/zconf.h

# define Bytef     z_Bytef 

Więc teraz jestem po prostu stracone.

+0

można użyć iostreams impuls jest: ma filtr zlib – akappa

Odpowiedz

6

podstawie this nutą qUncompress, myślę, że to całkiem proste.

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.

więc prawdopodobnie można po prostu ścisnąć go tak:

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

powiedział: „Dołącz”, ale wygląda na to kod usuwa pierwsze 4 bajty zamiast wkładania rozmiar z przodu. –

+1

Kod jest tym, co miałem na myśli. qUncompress oczekuje 4 niestandardowych bajtów, które zostały dodane. W związku z tym qCompress musi mieć przedrostek 4 niestandardowych bajtów. Jeśli używasz qCompress i usuwasz te bajty, powinieneś mieć tylko standardowe pliki zlib. – cgmb

+0

Próbowałem już tego. Kiedy używam qUncompress (poprzedzającego oczekiwany rozmiar), zlib skarży się, że jest uszkodzony. –

0

Oto pewien kod, który kiedyś napisałem, który dostaje jako wejście wskaźnik do tablicy bajtów, liczba bajtów do kompresji i poziom kompresji, a następnie używa zlib do kompresji danych wejściowych. Wynik jest zwracany w ciągu znaków.

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

Może to pomoże w rozwiązaniu problemu, myślę, używając cdata.constData() można bezpośrednio wywołać tę funkcję

0

Wystarczy, aby pomóc z ostatniego odcinka swoje pytanie tutaj:

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

Dla definicji Byte i Bytef, spójrz na linie 332 i 333 zconf.h, a także linię 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;  

Definicja FAR dotyczy programowania w trybie mieszanym MSDOS, w przeciwnym razie nie jest zdefiniowana jako nic (patrz wiersze 328-330 pliku zconf.h).

Tak więc zlib typedefs Bytef i Byte są zasadniczo takie same, jak unsigned char na większości platform.W związku z tym powinno być możliwe do wykonania następujących czynności:

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

Ten kod nie może skompilować stwierdzenia: error: invalid static_cast z typu 'char *' na typ 'unsigned char *' –

+0

OK, zastąpiłem 'static_cast <>()' z 'reinterpret_cast <>()' ... to zdecydowanie powinno się skompilować. – Jason