← На главную

Примеры сжатия данных в C/C++ при помощи zlib

Продолжая серию постов о полезных библиотеках в мире C/C++, стоило бы упомянуть хотя бы одну библиотеку для сжатия данных. Библиотек таких великое множество. Среди них, пожалуй, наиболее распространенной, своего рода стандартом де-факто, является zlib. Поэтому о ней далее речь и пойдет.

Пользуясь случаем, напомню, что в предыдущих сериях вы видели:

Теперь вернемся к zlib. Допустим, вам нужно сжать сравнительно небольшой кусок данных, целиком помещающийся в память. Делается это элементарно:

uLongf compress_buff_size = compressBound(file_size); void* compress_buff = malloc(compress_buff_size); if(compress_buff == NULL) { fprintf(stderr, "malloc(compress_buff_size) failed, " "compress_buff_size = %lu\n", compress_buff_size); exit(1); } uLongf compressed_size = compress_buff_size; res = compress(compress_buff, &compressed_size, file_buff, file_size); if(res != Z_OK) { fprintf(stderr, "compress(...) failed, res = %d\n", res); exit(1); }

Процедура compressBound возвращает максимальный размер, какого могут оказаться данные в сжатом виде, а compress непосредственно производит сжатие. В результате в compress_buff будут записаны данные в сжатом виде, а в compressed_size – их размер.

Разжатие производится по аналогии:

uLongf decompressed_size = (uLongf)file_size; res = uncompress(file_buff, &decompressed_size, compress_buff, compressed_size); if(res != Z_OK) { fprintf(stderr, "uncompress(...) failed, res = %d\n", res); exit(1); }

Компрессия и декомпрессия больших объемов данных производится чуть сложнее, но ненамного:

/* Компрессия */ bool compress_file(FILE *src, FILE *dst) { uint8_t inbuff[CHUNK_SIZE]; uint8_t outbuff[CHUNK_SIZE]; z_stream stream = {0}; if(deflateInit(&stream, COMPRESSION_LEVEL) != Z_OK) { fprintf(stderr, "deflateInit(...) failed!\n"); return false; } int flush; do { stream.avail_in = fread(inbuff, 1, CHUNK_SIZE, src); if(ferror(src)) { fprintf(stderr, "fread(...) failed!\n"); deflateEnd(&stream); return false; } flush = feof(src) ? Z_FINISH : Z_NO_FLUSH; stream.next_in = inbuff; do { stream.avail_out = CHUNK_SIZE; stream.next_out = outbuff; deflate(&stream, flush); uint32_t nbytes = CHUNK_SIZE - stream.avail_out; if(fwrite(outbuff, 1, nbytes, dst) != nbytes || ferror(dst)) { fprintf(stderr, "fwrite(...) failed!\n"); deflateEnd(&stream); return false; } } while (stream.avail_out == 0); } while (flush != Z_FINISH); deflateEnd(&stream); return true; } /* Декомпрессия */ bool decompress_file(FILE *src, FILE *dst) { uint8_t inbuff[CHUNK_SIZE]; uint8_t outbuff[CHUNK_SIZE]; z_stream stream = { 0 }; int result = inflateInit(&stream); if(result != Z_OK) { fprintf(stderr, "inflateInit(...) failed!\n"); return false; } do { stream.avail_in = fread(inbuff, 1, CHUNK_SIZE, src); if(ferror(src)) { fprintf(stderr, "fread(...) failed!\n"); inflateEnd(&stream); return false; } if(stream.avail_in == 0) break; stream.next_in = inbuff; do { stream.avail_out = CHUNK_SIZE; stream.next_out = outbuff; result = inflate(&stream, Z_NO_FLUSH); if(result == Z_NEED_DICT || result == Z_DATA_ERROR || result == Z_MEM_ERROR) { fprintf(stderr, "inflate(...) failed: %d\n", result); inflateEnd(&stream); return false; } uint32_t nbytes = CHUNK_SIZE - stream.avail_out; if(fwrite(outbuff, 1, nbytes, dst) != nbytes || ferror(dst)) { fprintf(stderr, "fwrite(...) failed!\n"); inflateEnd(&stream); return false; } } while (stream.avail_out == 0); } while (result != Z_STREAM_END); inflateEnd(&stream); return result == Z_STREAM_END; }

Долго и нудно разжевывать этот код мне что-то лень. Думаю, вы в состоянии самостоятельно в нем разобраться. В крайнем случае всегда можно почитать /usr/include/zlib.h – в нем есть подробные комментарии ко всем процедурам.

Полная версия исходников к этому посту лежит на GitHub. Для сборки проекта я использовал Autotools. Не помню, зачем мне нужен был именно Autotools, так как код писался давно. В README.md есть инструкция по сборке, плюс обратите внимание на пост Основы сборки проектов при помощи Autotools.

Как обычно, буду рад вашим вопросам и дополнениям.