Личный опыт разработки ПО

Сборник рецептов

Архивирование с библиотеками zlib и bzip2/libbzip2 используя Boost Iostreams

комментариев 9

Недавно у меня возникла задача добавить сжатие данных в программу. Сразу нашлось две свободных библиотеки – zlib и libbzip2 написанные на C. Изучив вопрос глубже, оказалось что писать удобные обертки над функциями на C не нужно, так как в Boost IOStreams все уже написано.

О том как добавить данный функционал и правильно собрать проект я расскажу в данной заметке. Также будет приведен пример кода для сжатия и распаковки файлов и сравнение zlib (алгоритм DEFLATE, методы gzip и zlib) и libbzip2 (алгоритм bzip2) по скорости работы и уровню сжатия тестовых, бинарных и исполняемых файлов.

Я не буду рассматривать интерфейсы данных библиотек, так как Boost предоставляет довольно удобную абстракцию. Суть абстракции в следующем – имеются устройства представляющие собой например файл отрытый для записи, фильтры, например архивирующий и есть возможность создать фильтрующий поток, содержащий устройство и цепочку из произвольных фильтров. Таким образом создав фильтр сжимающий переданные в него данные и соединив его с устройством пишущим в файл мы получим готовое решение для сжатия и записи данных. Соответственно чтобы распаковать сжатый файл, нам нужно создать устройство представляющее открытый для чтения файл, фильтр который будет распаковывать поступающие в него данные и соединить их.

Сразу приведу код, который рассмотрю ниже:

boost::iostreams::filtering_ostreambuf out;
out.push(boost::iostreams::bzip2_compressor());
out.push(boost::iostreams::file_sink("data.bin", std::ios::binary));
boost::iostreams::copy(boost::iostreams::file_source("data.bz2", std::ios::binary), out);
...
boost::iostreams::filtering_istreambuf in;
in.push(boost::iostreams::bzip2_decompressor());
in.push(boost::iostreams::file_source("data.bz2", std::ios::binary));
boost::iostreams::copy(in, boost::iostreams::file_sink("data.bin", std::ios::binary));

По сути в этих восьми строках содержится все необходимое для сжатия и распаковки файла. В обоих случаях мы создаем однонаправленный (только на чтение или только на запись) фильтрующий буфер потока. Создаем фильтр осуществляющий сжатие или распаковку, устройство представляющее файл, и соединяем их в буфере. После этого можно используя утилиту copy из Boost Iostreams копировать содержимое другого потока в буфер или наоборот.

Если вместо буфера (filtering_streambuf) использовать поток (filtering_stream), то писать или читать из него можно используя интерфейс потоков C++.

Устройства в Boost Iostream могут быть источниками (Source) или приемниками (Sink). Соответственно одни могут поставлять данные по требованию откуда либо, например из файла, а другие наоборот данные только куда то отправляют. Причем вы можете написать свой приемник или источник (это несложно) и создать свое устройство реализующее необходимую абстракцию. Например socket_source и socket_sink позволяющие работать с сокетами, pipe_source и pipe_sink тоже самое для именованных каналов или вообще null_sink (который кстати в Boost уже есть), которое просто получив данные тут же о них забывает. Причем соединив, скажем socket_source с фильтром boost::iostreams::bzip2_compressor вы получите передачу через сокеты сжатых данных, а дописав еще шифрующий фильтр можно получить передачу зашифрованных сжатых данных.

Сборка

Чтобы собрать ваш проект с Boost и одной из библиотек сжатия нужно сделать три вещи:

  1. Собрать библиотеку zlib или bzip2/libzip2 и убедиться, что ваша система сборки увидит путь к заголовочным файлам и собранным библиотекам. Кстати на большинстве Unix систем об этом можно не задумываться, так как скорее всего библиотека уже установлена.
  2. Установить при сборке проекта переменную компиляции BOOST_IOSTREAMS_NO_LIB
  3. Включить в сборку один из файлов из Boost (в версии 1.41.0 они находятся в libs/iostreams/src) в зависимости от используемого метода:
    • zlib – zlib.cpp
    • gzip – gzip.cpp
    • bzip2 – bzip2.cpp

Сравнение методов сжатия zlib/gzip/bzip2

Увлекшись я написал небольшой тест для сравнения описываемых методов. Вот результаты запуска программы:

Бинарный файл со случайными числами

Размер: 5 242 880 байт

Метод Время сжатия, секунд Время распаковки, секунд Размер сжатого файла, байт
zlib 0.20 0.15 5 053 306
gzip 0.18 0.15 5 053 318
bzip2 0.59 0.77 5 049 795

 

Текстовой файл (несколько мануалов Linux слитые в один файл)

Размер: 3 534 333 байт

Метод Время сжатия, секунд Время распаковки, секунд Размер сжатого файла, байт
zlib 0.28 0.07 1 050 711
gzip 0.36 0.08 1 050 723
bzip2 0.43 0.24 856 572

 

Исполняемый файл

Размер: 4 568 332 байт

Метод Время сжатия, секунд Время распаковки, секунд Размер сжатого файла, байт
zlib 1.51 0.10 1 636 939
gzip 1.61 0.10 1 636 951
bzip2 0.87 0.36 1 439 625

 

Тест конечно не претендует на серьезное исследование, но определенные выводы сделать позволяет:

  • bzip2 сжимает лучше, но при этом значительно медленнее.
  • zlib и gzip практически одинаковы. Это правда так, так как они используют один алгоритм, единственное отличие в том, что zlib оптимизирован для сжатия потоковых данных, например из-за этой особенности он используется в протоколе HTTP/1.1, в то время как gz применяется для сжатия файлов.

Файлы к заметке

По традиции вы можете скачать исходный код тестового проекта. Архив содержит код библиотек и файлы для сборки с CMake, таким образом вам нужно только иметь установленный Boost, Cmake и положить в директорию data проекта свои тестовые данные.

7th Март 2010
22:59

Рубрика: C++,Разработка

Метки:

9 комментариев к 'Архивирование с библиотеками zlib и bzip2/libbzip2 используя Boost Iostreams'

Подписаться на комментарии по RSS или TrackBack.

  1. 584reg

  2. 8cmdl0

  3. 5v68yz

  4. 72vnvp

  5. pq2422

  6. jkh4hq

  7. u9uje0

  8. t5x56h

  9. o4cslr

Оставить комментарий