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