Tutorial de la biblioteca Python zlib

La biblioteca Python zlib proporciona una interfaz de Python para la biblioteca zlib C, que es una abstracción de alto nivel para el algoritmo de compresión sin pérdidas DEFLATE....

¿Qué es Python zlib

La biblioteca Python zlib proporciona una interfaz Python para la biblioteca zlib C. algoritmo de compresión sin pérdidas. El formato de datos utilizado por la biblioteca se especifica en el RFC 1950 a 1952, disponible en http://www.ietf.org/rfc/rfc1950.txt>.

El formato de compresión zlib es de uso gratuito y no está cubierto por ninguna patente, por lo que también puede usarlo de manera segura en productos comerciales. Es un formato de compresión sin pérdidas (lo que significa que no pierde ningún dato entre la compresión y la descompresión), y tiene la ventaja de ser portátil entre diferentes plataformas. Otro beneficio importante de este mecanismo de compresión es que no expande los datos.

El uso principal de la biblioteca zlib es en aplicaciones que requieren compresión y descompresión de datos arbitrarios, ya sea una cadena, contenido estructurado en memoria o archivos.

Las funcionalidades más importantes incluidas en esta biblioteca son la compresión y la descompresión. La compresión y la descompresión se pueden realizar como operaciones únicas o mediante la división de los datos en fragmentos como si se tratara de un flujo de datos. Ambos modos de funcionamiento se explican en este artículo.

En mi opinión, una de las mejores cosas de la biblioteca zlib es que es compatible con el formato de archivo/herramienta gzip (que también se basa en DEFLATE ), que es una de las aplicaciones de compresión más utilizadas en los sistemas Unix.

Compresión

Comprimir una cadena de datos

La biblioteca zlib nos proporciona la función comprimir, que se puede usar para comprimir una cadena de datos. La sintaxis de esta función es muy simple, tomando solo dos argumentos:

1
compress(data, level=-1)

Aquí el argumento datos contiene los bytes a comprimir, y nivel es un valor entero que puede tomar los valores -1 o 0 a 9. Este parámetro determina el nivel de compresión, donde el nivel 1 es el más rápido y produce el nivel más bajo de compresión. El nivel 9 es el más lento, pero produce el mayor nivel de compresión. El valor -1 representa el valor predeterminado, que es el nivel 6. El valor predeterminado tiene un equilibrio entre velocidad y compresión. El nivel 0 no produce compresión.

A continuación se muestra un ejemplo del uso del método comprimir en una cadena simple:

1
2
3
4
5
6
7
8
9
import zlib
import binascii

data = 'Hello world'

compressed_data = zlib.compress(data, 2)

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

Y el resultado es el siguiente:

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

Figura 1

Si cambiamos el nivel a 0 (sin compresión), entonces la línea 5 se convierte en:

1
compressed_data = zlib.compress(data, 0)

Y el nuevo resultado es:

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

Figura 2

Puede notar algunas diferencias al comparar las salidas cuando usa 0 o 2 para el nivel de compresión. Usando un nivel de 2 obtenemos una cadena (formateada en hexadecimal) de longitud 38, mientras que con un nivel de 0 obtenemos una cadena hexadecimal con una longitud de 44. Esta diferencia en la longitud se debe a la falta de compresión al usar nivel 0.

Si no formatea la cadena como hexadecimal, como lo hice en este ejemplo, y ve los datos de salida, probablemente notará que la cadena de entrada aún se puede leer incluso después de haber sido "comprimida", aunque tiene algunos caracteres de formato adicionales a su alrededor.

Compresión de grandes flujos de datos

Los grandes flujos de datos se pueden gestionar con la función compressobj(), que devuelve un objeto de compresión. La sintaxis es la siguiente:

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

La principal diferencia entre los argumentos de esta función y la función compress() es (aparte del parámetro data) el argumento wbits, que controla el tamaño de la ventana y si el encabezado y el tráiler se incluyen o no en La salida.

Los valores posibles para wbits son:

Valor Tamaño de ventana logaritmo Salida


+9 a +15 Base 2 Incluye encabezado zlib y tráiler -9 a -15 Valor absoluto de wbits Sin encabezado ni tráiler +25 a +31 4 bits bajos del valor Incluye encabezado gzip y suma de comprobación final

Tabla 1

El argumento método representa el algoritmo de compresión utilizado. Actualmente, el único valor posible es DEFLATED, que es el único método definido en RFC 1950. El argumento strategy se relaciona con el ajuste de compresión. A menos que realmente sepa lo que está haciendo, le recomiendo que no lo use y solo use el valor predeterminado.

El siguiente código muestra cómo usar la función compressobj():

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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))

Después de ejecutar este código, el resultado es:

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

Figura 3

Como podemos ver en la figura anterior, la frase "Hola mundo" se comprimió. Por lo general, este método se usa para comprimir flujos de datos que no caben en la memoria a la vez. Aunque este ejemplo no tiene un flujo de datos muy grande, sirve para mostrar la mecánica de la función compressobj().

También puede ver cómo sería útil en una aplicación más grande en la que puede configurar la compresión y luego pasar el objeto de compresión a otros métodos/módulos. Esto se puede usar para comprimir fragmentos de datos en serie.

También puede ver cómo sería útil en un escenario en el que tiene un flujo de datos para comprimir. En lugar de tener que acumular todos los datos en la memoria, puede simplemente llamar a compress.compress(data) y compress.flush() en su fragmento de datos y luego pasar al siguiente fragmento mientras deja el anterior para ser limpiado por la recolección de basura.

Comprimir un archivo {#comprimir un archivo}

También podemos usar la función compress() para comprimir los datos en un archivo. La sintaxis es la misma que en el primer ejemplo.

En el siguiente ejemplo, comprimiremos un archivo de imagen PNG llamado "logo.png" (que, debo señalar, ya es una versión comprimida de la imagen sin formato original).

El código de ejemplo es el siguiente:

1
2
3
4
5
6
7
8
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))

En el código anterior, la línea zlib.compress(...) usa la constante Z_BEST_COMPRESSION, que, como sugiere el nombre, nos brinda el mejor nivel de compresión que este algoritmo tiene para ofrecer. La siguiente línea calcula el nivel de compresión en función de la relación entre la longitud de los datos comprimidos y la longitud de los datos originales.

El resultado es el siguiente:

1
2
$ python compress_file.py 
Compressed: 13%

Figura 4

Como podemos ver, el archivo estaba comprimido en un 13%.

La única diferencia entre este ejemplo y el primero es la fuente de los datos. Sin embargo, creo que es importante mostrarlo para que pueda tener una idea de qué tipo de datos se pueden comprimir, ya sea solo una cadena ASCII o datos de imágenes binarias. Simplemente lea sus datos del archivo como lo haría normalmente y llame al método comprimir.

Guardar datos comprimidos en un archivo {#guardar datos comprimidos en un archivo}

Los datos comprimidos también se pueden guardar en un archivo para su uso posterior. El siguiente ejemplo muestra cómo guardar texto comprimido en un archivo:

1
2
3
4
5
6
7
8
9
import zlib

my_data = 'Hello world'

compressed_data = zlib.compress(my_data, 2)

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

El ejemplo anterior comprime nuestra cadena simple "Hello world" y guarda los datos comprimidos en un archivo llamado "outfile.txt". El archivo "outfile.txt", cuando se abre con nuestro editor de texto, tiene el siguiente aspecto:

Archivo de cadena comprimido{.img-responsive}

Figura 5

Descompresión

Descompresión de una cadena de datos {#descompresión de una cadena de datos}

Una cadena comprimida de datos se puede descomprimir fácilmente usando la función descomprimir(). La sintaxis es la siguiente:

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

Esta función descomprime los bytes en el argumento data. El argumento wbits se puede usar para administrar el tamaño del búfer de historial. El valor predeterminado coincide con el tamaño de ventana más grande. También pide la inclusión del encabezado y el tráiler del archivo comprimido. Los valores posibles son:

Valor Tamaño de ventana logaritmo Entrada


+8 a +15 Base 2 Incluye encabezado zlib y tráiler -8 a -15 Valor absoluto de wbits Transmisión sin procesar sin encabezado ni tráiler +24 a +31 = 16 + (8 a 15) 4 bits bajos del valor Incluye encabezado gzip y tráiler +40 a +47 = 32 + (8 a 15) 4 bits bajos del valor formato zlib o gzip

Tabla 2

El valor inicial del tamaño del búfer se indica en el argumento bufsize. Sin embargo, el aspecto importante de este parámetro es que no necesita ser exacto, porque si se necesita un tamaño de búfer adicional, se incrementará automáticamente.

El siguiente ejemplo muestra cómo descomprimir la cadena de datos comprimida en nuestro ejemplo anterior:

1
2
3
4
5
6
7
8
import zlib

data = 'Hello world'

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

print('Decompressed data: ' + decompressed_data)

El resultado es el siguiente:

1
2
$ python decompress_str.py 
Decompressed data: Hello world

Figura 5

Descompresión de grandes flujos de datos {#descompresión de grandes flujos de datos}

La descompresión de grandes flujos de datos puede requerir la administración de la memoria debido al tamaño o la fuente de sus datos. Es posible que no pueda usar toda la memoria disponible para esta tarea (o que no tenga suficiente memoria), por lo que el método decompressobj() le permite dividir un flujo de datos en varios fragmentos que puede descomprimir por separado.

La sintaxis de la función decompressobj() es la siguiente:

1
decompressobj(wbits=15[, zdict])

Esta función devuelve un objeto de descompresión, que utiliza para descomprimir los datos individuales. El argumento wbits tiene las mismas características que en la función decompress() explicada anteriormente.

El siguiente código muestra cómo descomprimir una gran cantidad de datos almacenados en un archivo. En primer lugar, el programa crea un archivo llamado "outfile.txt", que contiene los datos comprimidos. Tenga en cuenta que los datos se comprimen usando un valor de wbits igual a +15. Esto asegura la creación de un encabezado y un tráiler en los datos.

Luego, el archivo se descomprime utilizando fragmentos de datos. Nuevamente, en este ejemplo, el archivo no contiene una gran cantidad de datos, pero sin embargo, sirve para explicar el concepto de búfer.

El código es el siguiente:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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()

Después de ejecutar el código anterior, obtenemos los siguientes resultados:

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

Figura 6

Descomprimir datos de un archivo

Los datos comprimidos contenidos en un archivo se pueden descomprimir fácilmente, como ha visto en ejemplos anteriores. Este ejemplo es muy similar al anterior en el sentido de que estamos descomprimiendo datos que se originan en un archivo, excepto que en este caso vamos a volver a usar el método decompress único, que descomprime los datos en una única llamada de método. Esto es útil cuando sus datos son lo suficientemente pequeños como para caber fácilmente en la memoria.

Esto se puede ver en el siguiente ejemplo:

1
2
3
4
5
import zlib

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

El programa anterior abre el archivo "compressed.dat" creado en un ejemplo anterior, que contiene la cadena "Hello world" comprimida.

En este ejemplo, una vez que los datos comprimidos se recuperan y almacenan en la variable compressed_data, el programa descomprime el flujo y muestra el resultado en la pantalla. Como el archivo contiene una pequeña cantidad de datos, el ejemplo utiliza la función descomprimir(). Sin embargo, como muestra el ejemplo anterior, también podríamos descomprimir los datos usando la función decompressobj().

Después de ejecutar el programa obtenemos el siguiente resultado:

1
2
$ python decompress_file.py 
Hello world

Figura 7

Finalizando

La biblioteca de Python zlib nos proporciona un conjunto útil de funciones para la compresión de archivos utilizando el formato zlib. Normalmente se utilizan las funciones compress() y decompress(). Sin embargo, cuando hay restricciones de memoria, las funciones compressobj() y decompressobj() están disponibles para brindar más flexibilidad al admitir la compresión/descompresión de flujos de datos. Estas funciones ayudan a dividir los datos en fragmentos más pequeños y manejables, que pueden comprimirse o descomprimirse usando las funciones compress() y decompress() respectivamente.

Tenga en cuenta que la biblioteca zlib también tiene bastantes funciones más de las que pudimos cubrir en este artículo. Por ejemplo, puede usar zlib para calcular la suma de verificación de algunos datos para verificar su integridad cuando se descomprimen. Para obtener más información sobre características adicionales como esta, consulte la [documentación oficial] (https://docs.python.org/3/library/zlib.html).

Licensed under CC BY-NC-SA 4.0