gzip - Legge e Scrive File zip GNU

Scopo: Il modulo gzip fornisce una interfaccia tipo file ai file zip GNU, usando zlib per comprimere e decomprimere i dati

Scrivere File Compressi

La funzione a livello di modulo open() crea una istanza della classe tipo file GzipFile. Vengono forniti gli usuali metodi per scrivere e leggere byte.

# gzip_write.py

import gzip
import io
import os

outfilename = 'un_esempio.txt.gz'
with gzip.open(outfilename, 'wb') as output:
    with io.TextIOWrapper(output, encoding='utf-8') as enc:
        enc.write('I contenuti del file di esempio vanno qui.\n')

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

Per scrivere dati in un file compresso, lo si apra con la modalità 'wb'. Questo esempio racchiude il file compresso con GzipFIle in un TextIOWrapper dal modulo io per codificare il testo Unicode in byte adatti alla compressione.

$ python3 gzip_write.py

un_esempio.txt.gz contiene 84 byte
application/gzip; charset=binary

E' possibile adottare diversi gradi di compressione passando l'argomento compresslevel. L'intervallo di valori va da 0 a 9 compreso. I valori più bassi rappresentano una compressione minore e più veloce. I valori più alti rappresentano una compressione maggiore e più lenta.

# 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('Livello  Dimensione        Checksum')
print('-------  ----------  ---------------------------------')
print('dati     {:>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 colonna centrale di numeri nel risultato mostra la dimensione in byte del file prodotti dalla compressione dell'input. Per questi dati in input, i valori di compressione più alti non necessariamente portano vantaggi in termini di minore spazio di conservazione utilizzato. I risultati varieranno in base ai dati in input.

$ python3 gzip_compresslevel.py

Livello  Dimensione        Checksum
-------  ----------  ---------------------------------
dati         754688  a2762eaf5010a19ffbffaa3968eb27ac
    0        754793  8d88732f01f93a5372db9bcefeb41bc1
    1         10168  562db232b20ce400a0f7a51668ed4693
    2          8380  7ddac936ba04b9adcb09b10e8ac7856e
    3         10022  b75a437c9ec1d4f496664f9c5d147171
    4          4168  9d14f00a1395c670cf671c5b64933d7d
    5          4168  b8b8c6cfe628cf1f7009b96158f6753c
    6          4168  f107f5d070d2b42e0cf3ab1deb45b4b8
    7          4168  d876aa6329b058ba81315f24dbed3975
    8          4168  16b2230cbf552201f787a8ae94b5ce1e
    9          4168  5a73a43e9383004570f96aae489fa290

Una istanza di GzipFile include anche un metodo writelines() che può essere usato per scrivere una sequenza di stringhe.

# gzip_writelines.py

import gzip
import io
import itertools
import os

with gzip.open('un_esempio_con_righe.txt.gz', 'wb') as output:
    with io.TextIOWrapper(output, encoding='utf-8') as enc:
        enc.writelines(
            itertools.repeat('La stessa riga, ripetuta 10 volte.\n',
                             10)
        )

os.system('gunzip -c un_esempio_con_righe.txt.gz')

Così come per i normali file, occorre fornire alle righe in input un carattere di ritorno a capo.

$ python3 gzip_writelines.py

La stessa riga, ripetuta 10 volte.
La stessa riga, ripetuta 10 volte.
La stessa riga, ripetuta 10 volte.
La stessa riga, ripetuta 10 volte.
La stessa riga, ripetuta 10 volte.
La stessa riga, ripetuta 10 volte.
La stessa riga, ripetuta 10 volte.
La stessa riga, ripetuta 10 volte.
La stessa riga, ripetuta 10 volte.
La stessa riga, ripetuta 10 volte.

Leggere Dati Compressi

Per leggere dati da file precedentemente compressi, si apra il file in modalità di lettura binaria ('rb') in modo che non venga effettuata alcuna traduzione di separatori di riga basata sul testo o decodifica Unicode.

# gzip_read.py

import gzip
import io

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

Questo esempio legge il file scritto da gzip_write.py dalla sezione precedente, usando un TextIOWrapper per decodificare il testo dopo la decompressione.

$ python3 gzip_read.py

I contenuti del file di esempio vanno qui.

Quando si legge un file, è anche possibile cercare di leggere solo parte dei dati.

# gzip_seek.py

import gzip

with gzip.open('un_esempio.txt.gz', 'rb') as input_file:
    print('Tutto il file:')
    all_data = input_file.read()
    print(all_data)

    expected = all_data[5:15]

    # si riporta ad inizio file
    input_file.seek(0)

    # avanti di 5 byte
    input_file.seek(5)
    print('A partire dalla posizione 5 per 10 byte:')
    partial = input_file.read(10)
    print(partial)

    print()
    print(expected == partial)

La posizione da raggiungere con seek() è relativa ai dati non compressi, quindi il chiamante non deve sapere che il file dati è compresso.

$ python3 gzip_seek.py

Tutto il file:
b'I contenuti del file di esempio vanno qui.\n'
A partire dalla posizione 5 per 10 byte:
b'tenuti del'

True

Lavorare con i Flussi

La classe GzipFile può essere usata per incapsulare altri tipi di flussi di dati in modo che anch'essi possano usufruire della compressione. Questo è utile quando i flussi vengono trasmessi attraverso un socket oppure un handle di file esistente (già aperto). Può anche essere usato un buffer BytesIO.

# gzip_BytesIO.py

import gzip
from io import BytesIO
import binascii

uncompressed_data = b'La stessa riga, ripetuta 10 volte.\n' * 10
print('NON COMPRESSO:', 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('COMPRESSO:', 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('\nRILETTURA:', len(reread_data))
print(reread_data)

Un vantaggio dell'uso di GzipFile in luogo di zlib è che supporta le API dei file. Comunque quando vengono riletti dati precedentemente compressi, viene passata una esplicita lunghezza a read(). Non fornendo la lunghezza si incorre in un errore CRC, probabilmente perchè BytesIO ritornerebbe una stringa vuota prima di segnalare il carattere di fine file (EOF). Quando si lavora con flussi di dati compressi, si facciano precedere i dati con un intero che rappresenta l'effettivo ammontare di dati da leggere oppure si utilizzi l'API di decompressione incrementale in zlib.

$ python3 gzip_BytesIO.py

NON COMPRESSO: 350
b'La stessa riga, ripetuta 10 volte.\nLa stessa riga, ripetuta 10 volte.\nLa stessa riga, ripetuta 10 volte.\nLa stessa riga, ripetuta 10 volte.\nLa stessa riga, ripetuta 10 volte.\nLa stessa riga, ripetuta 10 volte.\nLa stessa riga, ripetuta 10 volte.\nLa stessa riga, ripetuta 10 volte.\nLa stessa riga, ripetuta 10 volte.\nLa stessa riga, ripetuta 10 volte.\n'
COMPRESSO: 59
b'1f8b08005e81b86002fff34954282e492d2e4e5428ca4c4fd4019205a925a525890a86060a65f93925a97a5c3ea34ac8550200ad9a014b5e010000'

RILETTURA: 350
b'La stessa riga, ripetuta 10 volte.\nLa stessa riga, ripetuta 10 volte.\nLa stessa riga, ripetuta 10 volte.\nLa stessa riga, ripetuta 10 volte.\nLa stessa riga, ripetuta 10 volte.\nLa stessa riga, ripetuta 10 volte.\nLa stessa riga, ripetuta 10 volte.\nLa stessa riga, ripetuta 10 volte.\nLa stessa riga, ripetuta 10 volte.\nLa stessa riga, ripetuta 10 volte.\n'

Vedere anche:

gzip
La documentazione della libreria standard per questo modulo.
zlib
Il modulo zlib è una interfaccia a più basso livello alla compressione gzip.
zipfile
Il modulo zipfile fornisce accesso agli archivi ZIP.
bz2
Il modulo bz2 usa il formato di compressione bz2
tarfile
Il modulo tarfile include supporto built-in per leggere archivi tar compressi.
io
Costruzioni per creare condutture in input ed output