gzip — Leer y escribir archivos GNU zip

Propósito:Leer y escribir archivos zip de GNU.

El módulo gzip proporciona una interfaz similar a un archivo para archivos GNU zip, usando zlib para comprimir y descomprimir los datos.

Escribir archivos comprimidos

La función de nivel de módulo open() crea una instancia de tipo de archivo GzipFile. Se proporcionan los métodos habituales para escribir y leer bytes.

gzip_write.py
import gzip
import io
import os

outfilename = 'example.txt.gz'
with gzip.open(outfilename, 'wb') as output:
    with io.TextIOWrapper(output, encoding='utf-8') as enc:
        enc.write('Contents of the example file go here.\n')

print(outfilename, 'contains', os.stat(outfilename).st_size,
      'bytes')
os.system('file -b --mime {}'.format(outfilename))

Para escribir datos en un archivo comprimido, abre el archivo con el modo 'wb'. Este ejemplo envuelve el GzipFile con un TextIOWrapper del módulo io para codificar texto Unicode a bytes adecuado para compresión.

$ python3 gzip_write.py

application/x-gzip; charset=binary
example.txt.gz contains 75 bytes

Se pueden utilizar diferentes cantidades de compresión pasando un argumento compresslevel. Los valores válidos van de 0 a 9, inclusive. Los valores más bajos son más rápidos y dan como resultado una menor compresión. Valores mas altos son más lentos y comprimen más, hasta cierto punto.

gzip_compresslevel.py
import gzip
import io
import os
import hashlib


def get_hash(data):
    return hashlib.md5(data).hexdigest()


data = open('lorem.txt', 'r').read() * 1024
cksum = get_hash(data.encode('utf-8'))


print('Level  Size        Checksum')
print('-----  ----------  ---------------------------------')
print('data   {:>10}  {}'.format(len(data), cksum))

for i in range(0, 10):
    filename = 'compress-level-{}.gz'.format(i)
    with gzip.open(filename, 'wb', compresslevel=i) as output:
        with io.TextIOWrapper(output, encoding='utf-8') as enc:
            enc.write(data)
    size = os.stat(filename).st_size
    cksum = get_hash(open(filename, 'rb').read())
    print('{:>5d}  {:>10d}  {}'.format(i, size, cksum))

La columna central de números en la salida muestra el tamaño en bytes de los archivos producidos comprimiendo la entrada. Para estos datos de entrada, los valores de compresión más altos no necesariamente se pagan en disminución de espacio de almacenamiento. Los resultados variarán, dependiendo de los datos de entrada.

$ python3 gzip_compresslevel.py

Nivel  Tamaño      Suma de control
-----  ----------  ---------------------------------
data       754688  e4c0f9433723971563f08a458715119c
    0      754793  ced7189c324eb73a8388492a9024d391
    1        9846  5356d357f23e0d5b6d85e920929f0e43
    2        8267  8ce46bce238edc095e47e941cebad93d
    3        8227  91662517459db94a744671a6b4295b67
    4        4167  ad304e3aec585640de9f14306fb32083
    5        4167  4381a5d6dff4dd2746387f20411dcfcd
    6        4167  ef3a05112ea382abb53bc4a5bee3a52a
    7        4167  4723a253d1dc8ddecd4ff7b7adf0bc0b
    8        4167  0e1aeba7bdc39f0007039f130d9a28b2
    9        4167  eccf47c4c4f1cca3274e57a1b9b9ddd2

Una instancia GzipFile también incluye un método writelines() que se puede utilizar para escribir una secuencia de cadenas.

gzip_writelines.py
import gzip
import io
import itertools
import os

with gzip.open('example_lines.txt.gz', 'wb') as output:
    with io.TextIOWrapper(output, encoding='utf-8') as enc:
        enc.writelines(
            itertools.repeat('The same line, over and over.\n',
                             10)
        )

os.system('gzcat example_lines.txt.gz')

Al igual que con un archivo normal, las líneas de entrada deben incluir un carácter de nueva línea.

$ python3 gzip_writelines.py

The same line, over and over.
The same line, over and over.
The same line, over and over.
The same line, over and over.
The same line, over and over.
The same line, over and over.
The same line, over and over.
The same line, over and over.
The same line, over and over.
The same line, over and over.

Leer datos comprimidos

Para volver a leer datos de archivos previamente comprimidos, abre el archivo con modo de lectura binaria ('rb') así que no hay traducción de las terminaciones de línea o decodificación Unicode.

gzip_read.py
import gzip
import io

with gzip.open('example.txt.gz', 'rb') as input_file:
    with io.TextIOWrapper(input_file, encoding='utf-8') as dec:
        print(dec.read())

Este ejemplo lee el archivo escrito por gzip_write.py de la sección anterior, usar un TextIOWrapper para decodificar el texto después de que esta descomprimido.

$ python3 gzip_read.py

Contents of the example file go here.

Al leer un archivo, también es posible buscar y leer solo parte de los datos.

gzip_seek.py
import gzip

with gzip.open('example.txt.gz', 'rb') as input_file:
    print('Entire file:')
    all_data = input_file.read()
    print(all_data)

    expected = all_data[5:15]

    # rewind to beginning
    input_file.seek(0)

    # move ahead 5 bytes
    input_file.seek(5)
    print('Starting at position 5 for 10 bytes:')
    partial = input_file.read(10)
    print(partial)

    print()
    print(expected == partial)

La posición seek() es relativa a los datos sin comprimir, por lo que la persona que llama no necesita saber que el archivo de datos está comprimido.

$ python3 gzip_seek.py

Entire file:
b'Contents of the example file go here.\n'
Starting at position 5 for 10 bytes:
b'nts of the'

True

Trabajar con Flujos

La clase GzipFile puede usarse para envolver otros tipos de flujos de datos para que puedan usar la compresión también. Esto es útil cuando los datos se están transmitiendo a través de un conector o una manejador existente de archivo (ya abierto). Un buffer BytesIO también puede ser usado.

gzip_BytesIO.py
import gzip
from io import BytesIO
import binascii

uncompressed_data = b'The same line, over and over.\n' * 10
print('UNCOMPRESSED:', len(uncompressed_data))
print(uncompressed_data)

buf = BytesIO()
with gzip.GzipFile(mode='wb', fileobj=buf) as f:
    f.write(uncompressed_data)

compressed_data = buf.getvalue()
print('COMPRESSED:', len(compressed_data))
print(binascii.hexlify(compressed_data))

inbuffer = BytesIO(compressed_data)
with gzip.GzipFile(mode='rb', fileobj=inbuffer) as f:
    reread_data = f.read(len(uncompressed_data))

print('\nREREAD:', len(reread_data))
print(reread_data)

Una ventaja de usar GzipFile en lugar de zlib es que éste soporta la interfaz de programación de archivo. Sin embargo, al volver a leer datos comprimidos con anterioridad, se pasa una longitud explícita a read(). Dejando la longitud fuera resultó en un error de CRC, posiblemente porque BytesIO devolvió una cadena vacía antes de informar EOF. Al trabajar con flujos de datos comprimidos, ya sea prefijando los datos con un número entero que representa la cantidad real de datos para leer o usar la interfaz de programación de descompresión incremental en zlib.

$ python3 gzip_BytesIO.py

UNCOMPRESSED: 300
b'The same line, over and over.\nThe same line, over and over.\nT
he same line, over and over.\nThe same line, over and over.\nThe
same line, over and over.\nThe same line, over and over.\nThe sam
e line, over and over.\nThe same line, over and over.\nThe same l
ine, over and over.\nThe same line, over and over.\n'
COMPRESSED: 51
b'1f8b080022caae5a02ff0bc94855284ecc4d55c8c9cc4bd551c82f4b2d5248c
c4b0133f4b8424665916401d3e717802c010000'

REREAD: 300
b'The same line, over and over.\nThe same line, over and over.\nT
he same line, over and over.\nThe same line, over and over.\nThe
same line, over and over.\nThe same line, over and over.\nThe sam
e line, over and over.\nThe same line, over and over.\nThe same l
ine, over and over.\nThe same line, over and over.\n'

Ver también

  • Standard library documentation for gzip
  • zlib – El módulo zlib es una interfaz de bajo nivel a la compresión gzip.
  • zipfile – El módulo zipfile otorga accesso a archivos ZIP.
  • bz2 – El módulo bz2 usa el formato de compresión bzip2.
  • tarfile – El módulo tarfile incluye soporte incorporado para leer archivos tar comprimidos.
  • io – Bloques de construcción para la creación de flujos de entrada y salida.