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

gzip – чтение и запись zip-файлов GNU

Автор оригинала: Doug Hellmann.

Цель:

Чтение и запись файлов gzip.

Модуль gzip предоставляет файловый интерфейс для файлов GNU zip, используя zlib для сжатия и распаковки данных.

Запись сжатых файлов

Функция уровня модуля open () создает экземпляр файлового класса GzipFile . Предоставляются обычные методы записи и чтения байтов.

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))

Чтобы записать данные в сжатый файл, откройте файл в режиме 'wb' . В этом примере GzipFile заключен в оболочку TextIOWrapper из модуля io для кодирования текста Unicode в байты, подходящие для сжатия.

$ python3 gzip_write.py

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

Можно использовать различные степени сжатия, передав аргумент compresslevel . Допустимые значения от 0 до 9 включительно. Более низкие значения быстрее и приводят к меньшему сжатию. Более высокие значения медленнее и сжимаются сильнее, вплоть до определенного момента.

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', compressleveli) 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))

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

$ python3 gzip_compresslevel.py

Level  Size        Checksum
-----  ----------  ---------------------------------
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

Экземпляр GzipFile также включает метод writelines () , который можно использовать для записи последовательности строк.

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')

Как и в случае с обычным файлом, строки ввода должны включать новую строку

$ 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.

Чтение сжатых данных

Чтобы прочитать данные из ранее сжатых файлов, откройте файл в двоичном режиме чтения ( 'rb' ), чтобы не выполнялось текстовое преобразование окончаний строк или декодирование 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())

В этом примере выполняется чтение файла, записанного gzip_write.py из предыдущего раздела, с использованием TextIOWrapper для декодирования текста после его распаковки.

$ python3 gzip_read.py

Contents of the example file go here.

Во время чтения файла также можно искать и читать

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)

Положение seek () относится к несжатым данным, поэтому вызывающий

$ 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

Работа с потоками

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

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', fileobjbuf) 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', fileobjinbuffer) as f:
    reread_data  f.read(len(uncompressed_data))

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

Одним из преимуществ использования GzipFile над zlib является то, что он поддерживает файловый API. Однако при повторном чтении ранее сжатых данных явная длина передается в read () . Отсутствие длины привело к ошибке CRC, возможно потому, что BytesIO вернул пустую строку перед сообщением EOF. При работе с потоками сжатых данных либо используйте префикс данных с целым числом, представляющим фактический объем данных для чтения, либо используйте API инкрементной декомпрессии.

$ 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'

Смотрите также