Рубрики
Без рубрики

Учебник по библиотеке Python zlib

Автор оригинала: Scott Robinson.

Что такое Python zlib

Библиотека Python zlib предоставляет интерфейс Python для библиотеки zlib C, которая является абстракцией более высокого уровня для алгоритма сжатия DEFLATE lossless. Формат данных, используемый библиотекой, указан в RFC 1950-1952, который доступен по адресу http://www.ietf.org/rfc/rfc1950.txt .

Формат сжатия zlib является бесплатным для использования и не подпадает под действие какого-либо патента, поэтому вы можете безопасно использовать его и в коммерческих продуктах. Это формат сжатия без потерь (что означает, что вы не теряете никаких данных между сжатием и декомпрессией) и имеет то преимущество, что он переносим на разные платформы. Еще одним важным преимуществом этого механизма сжатия является то, что он не расширяет данные.

Основное применение библиотеки zlib заключается в приложениях, требующих сжатия и распаковки произвольных данных, будь то строка, структурированное содержимое в памяти или файлы.

Наиболее важными функциями, включенными в эту библиотеку, являются сжатие и декомпрессия. Сжатие и декомпрессия могут выполняться как единовременные операции, так и путем разделения данных на куски, как это может показаться из потока данных. Оба режима работы описаны в этой статье.

Одна из лучших вещей, на мой взгляд, в библиотеке zlib заключается в том, что она совместима с инструментом gzip file format/(который также основан на DEFLATE), который является одним из наиболее широко используемых приложений сжатия в системах Unix.

Компрессия

Сжатие строки данных

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

compress(data, level=-1)

Здесь аргумент data содержит байты, подлежащие сжатию, а level – целочисленное значение, которое может принимать значения от -1 или от 0 до 9. Этот параметр определяет уровень сжатия, где уровень 1 является самым быстрым и дает самый низкий уровень сжатия. Уровень 9-самый медленный, но он дает самый высокий уровень сжатия. Значение -1 представляет собой значение по умолчанию, то есть уровень 6. Значение по умолчанию имеет баланс между скоростью и сжатием. Уровень 0 не дает никакого сжатия.

Ниже приведен пример использования метода compress для простой строки:

import zlib
import binascii

data = 'Hello world'

compressed_data = zlib.compress(data, 2)

print('Original data: ' +  data)
print('Compressed data: ' + binascii.hexlify(compressed_data))

И в результате получается следующее:

$ python compress_str.py 
Original data: Hello world
Compressed data: 785ef348cdc9c95728cf2fca49010018ab043d

Рисунок 1

Если мы изменим уровень на 0 (без сжатия), то строка 5 станет:

compressed_data = zlib.compress(data, 0)

И вот новый результат:

$ python compress_str.py 
Original data: Hello world
Compressed data: 7801010b00f4ff48656c6c6f20776f726c6418ab043d

Рисунок 2

Вы можете заметить несколько различий, сравнивая выходы при использовании 0 или 2 для уровня сжатия. Использование уровня 2 мы получаем строку (отформатированную в шестнадцатеричном формате) длиной 38, тогда как с уровнем 0 мы получаем шестнадцатеричную строку длиной 44. Эта разница в длине обусловлена отсутствием сжатия при использовании уровня 0 .

Если вы не форматируете строку как шестнадцатеричную, как я сделал в этом примере, и не просматриваете выходные данные, вы, вероятно, заметите, что входная строка все еще читается даже после “сжатия”, хотя вокруг нее есть несколько дополнительных символов форматирования.

Сжатие Больших Потоков Данных

Большими потоками данных можно управлять с помощью функции compression () , которая возвращает объект сжатия. Синтаксис выглядит следующим образом:

compressobj(level=-1, method=DEFLATED, wbits=15, memLevel=8, strategy=Z_DEFAULT_STRATEGY[, zdict])

Основное различие между аргументами этой функции и функцией compress() заключается (помимо параметра data ) в аргументе wbits , который управляет размером окна и тем, включены ли заголовок и трейлер в выходные данные.

Возможные значения для bits :

от +9 до +15 Включает в себя заголовок zlib и трейлер База 2
от -9 до -15 Без жатки и прицепа Абсолютное значение битов
от +25 до +31 Включает в себя заголовок gzip и конечную контрольную сумму Низкие 4 бита значения

Таблица 1

Аргумент method представляет используемый алгоритм сжатия. В настоящее время единственным возможным значением является DEFLATED , который является единственным методом, определенным в RFC 1950. Аргумент strategy относится к настройке сжатия. Если вы действительно не знаете, что делаете, я бы рекомендовал не использовать его и просто использовать значение по умолчанию.

В следующем коде показано, как использовать функцию compressor() :

import zlib
import binascii

data = 'Hello world'

compress = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -15)
compressed_data = compress.compress(data)
compressed_data += compress.flush()

print('Original: ' + data)
print('Compressed data: ' + binascii.hexlify(compressed_data))

После запуска этого кода результат будет следующим:

$ python compress_obj.py 
Original: Hello world
Compressed data: f348cdc9c95728cf2fca490100

Рисунок 3

Как видно из рисунка выше, фраза “Hello world” была сжата. Обычно этот метод используется для сжатия потоков данных, которые не помещаются в память сразу. Хотя этот пример не имеет очень большого потока данных, он служит для демонстрации механики функции compressobj () .

Вы также можете увидеть, как это было бы полезно в более крупном приложении, в котором вы можете настроить сжатие, а затем передать объект сжатия другим методам/модулям. Затем это можно использовать для последовательного сжатия фрагментов данных.

Вы также можете увидеть, как это было бы полезно в сценарии, где у вас есть поток данных для сжатия. Вместо того чтобы накапливать все данные в памяти, вы можете просто вызвать compress.compress(data) и compress.flush() на вашем фрагменте данных, а затем переходите к следующему фрагменту, оставляя предыдущий для очистки сборкой мусора.

Сжатие файла

Мы также можем использовать функцию compress() для сжатия данных в файле. Синтаксис такой же, как и в первом примере.

В приведенном ниже примере мы будем сжимать файл PNG-изображения с именем “logo.png” (который, должен заметить, уже является сжатой версией исходного raw-изображения).

Пример кода выглядит следующим образом:

import zlib

original_data = open('logo.png', 'rb').read()
compressed_data = zlib.compress(original_data, zlib.Z_BEST_COMPRESSION)

compress_ratio = (float(len(original_data)) - float(len(compressed_data))) / float(len(original_data))

print('Compressed: %d%%' % (100.0 * compress_ratio))

В приведенном выше коде строка zlib.compress(...) использует константу Z_BEST_COMPRESSION , которая , как следует из названия, дает нам наилучший уровень сжатия, который может предложить этот алгоритм. Затем следующая строка вычисляет уровень сжатия на основе отношения длины сжатых данных к длине исходных данных.

В результате получается следующее:

$ python compress_file.py 
Compressed: 13%

Рисунок 4

Как мы видим, файл был сжат на 13%.

Единственное различие между этим примером и нашим первым-это источник данных. Тем не менее, я думаю, что это важно показать, чтобы вы могли получить представление о том, какие данные могут быть сжаты, будь то просто строка ASCII или двоичные данные изображения. Просто считайте данные из файла, как обычно, и вызывайте метод compress .

Сохранение сжатых данных в файл

Сжатые данные также могут быть сохранены в файл для последующего использования. В приведенном ниже примере показано, как сохранить сжатый текст в файл:

import zlib

my_data = 'Hello world'

compressed_data = zlib.compress(my_data, 2)

f = open('outfile.txt', 'w')
f.write(compressed_data)
f.close()

Приведенный выше пример сжимает нашу простую строку “Hello world” и сохраняет сжатые данные в файл с именем “outfile.txt”. В “outfile.txt” файл, открытый с помощью нашего текстового редактора, выглядит следующим образом:

Сжатый Строковый Файл

Рисунок 5

Декомпрессия

Распаковка строки данных

Сжатую строку данных можно легко распаковать с помощью функции decompress () . Синтаксис выглядит следующим образом:

decompress(data, wbits=MAX_WBITS, bufsize=DEF_BUF_SIZE)

Эта функция распаковывает байты в аргументе data . Аргумент bits можно использовать для управления размером буфера истории. Значение по умолчанию соответствует самому большому размеру окна. Он также запрашивает включение заголовка и трейлера сжатого файла. Возможные значения таковы:

Включает в себя заголовок zlib и трейлер от +8 до +15 База 2
Необработанный поток без заголовка и прицепа от -8 до -15 Абсолютное значение битов
Включает в себя заголовок gzip и трейлер от +24 до + (от 8 до 15) Низкие 4 бита значения
формат zlib или gzip от +40 до + (от 8 до 15) Низкие 4 бита значения

Таблица 2

Начальное значение размера буфера указывается в аргументе bufsize . Однако важным аспектом этого параметра является то, что он не должен быть точным, потому что если потребуется дополнительный размер буфера, он будет автоматически увеличен.

В следующем примере показано, как распаковать строку данных, сжатую в нашем предыдущем примере:

import zlib

data = 'Hello world'

compressed_data = zlib.compress(data, 2)
decompressed_data = zlib.decompress(compressed_data)

print('Decompressed data: ' + decompressed_data)

В результате получается следующее:

$ python decompress_str.py 
Decompressed data: Hello world

Рисунок 5

Распаковка Больших Потоков Данных

Распаковка больших потоков данных может потребовать управления памятью из-за размера или источника ваших данных. Возможно, вы не сможете использовать всю доступную память для этой задачи (или у вас недостаточно памяти), поэтому метод decompressobj() позволяет разделить поток данных на несколько блоков, которые можно распаковать отдельно.

Синтаксис функции decompressobj() выглядит следующим образом:

decompressobj(wbits=15[, zdict])

Эта функция возвращает объект декомпрессии, который вы используете для декомпрессии отдельных данных. Аргумент bits имеет те же характеристики, что и в функции decompress () , описанной ранее.

Следующий код показывает, как распаковать большой поток данных, хранящихся в файле. Во – первых, программа создает файл с именем “outfile.txt”, который содержит сжатые данные. Обратите внимание, что данные сжимаются с использованием значения bits , равного +15. Это обеспечивает создание заголовка и трейлера в данных.

Затем файл распаковывается с использованием фрагментов данных. Опять же, в этом примере файл не содержит большого количества данных, но тем не менее он служит для объяснения концепции буфера.

Код выглядит следующим образом:

import zlib

data = 'Hello world'

compress = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, +15)
compressed_data = compress.compress(data)
compressed_data += compress.flush()

print('Original: ' + data)
print('Compressed data: ' + compressed_data)

f = open('compressed.dat', 'w')
f.write(compressed_data)
f.close()

CHUNKSIZE = 1024

data2 = zlib.decompressobj()
my_file = open('compressed.dat', 'rb')            
buf = my_file.read(CHUNKSIZE)

# Decompress stream chunks
while buf:
    decompressed_data = data2.decompress(buf)
    buf = my_file.read(CHUNKSIZE)

decompressed_data += data2.flush()

print('Decompressed data: ' + decompressed_data)

my_file.close()

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

$ python decompress_data.py 
Original: Hello world
Compressed data: x??H???W(?/?I?=
Decompressed data: Hello world

Рисунок 6

Распаковка данных из файла

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

Это видно из следующего примера:

import zlib

compressed_data = open('compressed.dat', 'rb').read()
decompressed_data = zlib.decompress(compressed_data)
print(decompressed_data)

Приведенная выше программа открывает файл “compressed.data”, созданный в предыдущем примере, который содержит сжатую строку “Hello world”.

В этом примере, как только сжатые данные извлекаются и сохраняются в переменной compressed_data , программа распаковывает поток и показывает результат на экране. Поскольку файл содержит небольшое количество данных, в примере используется функция decompress () . Однако, как показывает предыдущий пример, мы также можем распаковать данные с помощью функции decompressobj () .

После запуска программы мы получаем следующий результат:

$ python decompress_file.py 
Hello world

Рисунок 7

Обертывание

Библиотека Python zlib предоставляет нам полезный набор функций для сжатия файлов с использованием формата zlib. Обычно используются функции compress() и decompress () . Однако при наличии ограничений памяти функции compressobj() и decompressobj() доступны для обеспечения большей гибкости за счет поддержки сжатия/декомпрессии потоков данных. Эти функции помогают разделить данные на более мелкие и более управляемые фрагменты, которые могут быть сжаты или распакованы с помощью функций compress() и decompress() соответственно.

Имейте в виду, что библиотека zlib также имеет гораздо больше функций, чем то, что мы смогли рассмотреть в этой статье. Например, вы можете использовать zlib для вычисления контрольной суммы некоторых данных, чтобы проверить их целостность при распаковке. Для получения дополнительной информации о дополнительных функциях, подобных этой, ознакомьтесь с официальной документацией .