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

zipfile – Чтение и запись ZIP-архивов в Python

Модуль zipfile можно использовать для управления файлами ZIP-архивов, форматом, популяризированным программой PKZIP для ПК.

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

Цель:

Чтение и запись файлов ZIP-архивов.

Модуль zipfile можно использовать для управления файлами ZIP-архивов, форматом, популяризированным программой PKZIP для ПК.

Тестирование файлов ZIP

Функция is_zipfile () возвращает логическое значение, указывающее, относится ли имя файла, переданное в качестве аргумента, к допустимому ZIP-архиву.

zipfile_is_zipfile.py

import zipfile

for filename in ['README.txt', 'example.zip',
                 'bad_example.zip', 'notthere.zip']:
    print('{:>15}  {}'.format(
        filename, zipfile.is_zipfile(filename)))

Если файл вообще не существует, is_zipfile () возвращает False .

$ python3 zipfile_is_zipfile.py

     README.txt  False
    example.zip  True
bad_example.zip  False
   notthere.zip  False

Чтение метаданных из архива

Используйте класс ZipFile для работы непосредственно с ZIP-архивом. Он поддерживает методы для чтения данных о существующих архивах, а также для изменения архивов путем добавления дополнительных файлов.

zipfile_namelist.py

import zipfile

with zipfile.ZipFile('example.zip', 'r') as zf:
    print(zf.namelist())

Метод namelist () возвращает имена файлов в существующем архиве.

$ python3 zipfile_namelist.py

['README.txt']

Однако список имен – это лишь часть информации, доступной из архива. Чтобы получить доступ ко всем метаданным о содержимом ZIP, используйте методы infolist () или getinfo () .

zipfile_infolist.py

import datetime
import zipfile


def print_info(archive_name):
    with zipfile.ZipFile(archive_name) as zf:
        for info in zf.infolist():
            print(info.filename)
            print('  Comment     :', info.comment)
            mod_date  datetime.datetime(*info.date_time)
            print('  Modified    :', mod_date)
            if info.create_system  0:
                system  'Windows'
            elif info.create_system  3:
                system  'Unix'
            else:
                system  'UNKNOWN'
            print('  System      :', system)
            print('  ZIP version :', info.create_version)
            print('  Compressed  :', info.compress_size, 'bytes')
            print('  Uncompressed:', info.file_size, 'bytes')
            print()


if __name__  '__main__':
    print_info('example.zip')

Есть и дополнительные поля, помимо напечатанных здесь, но расшифровка значений во что-нибудь полезное требует внимательного чтения Замечания по применению PKZIP со спецификацией файла ZIP.

$ python3 zipfile_infolist.py

README.txt
  Comment     : b''
  Modified    : 2010-11-15 06:48:02
  System      : Unix
  ZIP version : 30
  Compressed  : 65 bytes
  Uncompressed: 76 bytes

Если имя элемента архива известно заранее, его объект ZipInfo можно получить напрямую с помощью getinfo () .

zipfile_getinfo.py

import zipfile

with zipfile.ZipFile('example.zip') as zf:
    for filename in ['README.txt', 'notthere.txt']:
        try:
            info  zf.getinfo(filename)
        except KeyError:
            print('ERROR: Did not find {} in zip file'.format(
                filename))
        else:
            print('{} is {} bytes'.format(
                info.filename, info.file_size))

Если элемент архива отсутствует, getinfo () вызывает KeyError .

$ python3 zipfile_getinfo.py

README.txt is 76 bytes
ERROR: Did not find notthere.txt in zip file

Извлечение заархивированных файлов из архива

Чтобы получить доступ к данным из члена архива, используйте метод read () , передав имя члена.

zipfile_read.py

import zipfile

with zipfile.ZipFile('example.zip') as zf:
    for filename in ['README.txt', 'notthere.txt']:
        try:
            data  zf.read(filename)
        except KeyError:
            print('ERROR: Did not find {} in zip file'.format(
                filename))
        else:
            print(filename, ':')
            print(data)
        print()

При необходимости данные автоматически распаковываются.

$ python3 zipfile_read.py

README.txt :
b'The examples for the zipfile module use \nthis file and exampl
e.zip as data.\n'

ERROR: Did not find notthere.txt in zip file

Создание новых архивов

Чтобы создать новый архив, создайте экземпляр ZipFile с режимом 'w' . Любой существующий файл обрезается и запускается новый архив. Для добавления файлов используйте метод write () .

zipfile_write.py

from zipfile_infolist import print_info
import zipfile

print('creating archive')
with zipfile.ZipFile('write.zip', mode'w') as zf:
    print('adding README.txt')
    zf.write('README.txt')

print()
print_info('write.zip')

По умолчанию содержимое архива не сжимается.

$ python3 zipfile_write.py

creating archive
adding README.txt

README.txt
  Comment     : b''
  Modified    : 2016-08-07 13:31:24
  System      : Unix
  ZIP version : 20
  Compressed  : 76 bytes
  Uncompressed: 76 bytes

Для добавления сжатия требуется модуль zlib. Если доступен zlib, режим сжатия для отдельных файлов или для архива в целом можно установить с помощью zipfile.ZIP_DEFLATED . Режим сжатия по умолчанию – zipfile.ZIP_STORED , который добавляет входные данные в архив без сжатия.

zipfile_write_compression.py

from zipfile_infolist import print_info
import zipfile
try:
    import zlib
    compression  zipfile.ZIP_DEFLATED
except (ImportError, AttributeError):
    compression  zipfile.ZIP_STORED

modes  {
    zipfile.ZIP_DEFLATED: 'deflated',
    zipfile.ZIP_STORED: 'stored',
}

print('creating archive')
with zipfile.ZipFile('write_compression.zip', mode'w') as zf:
    mode_name  modes[compression]
    print('adding README.txt with compression mode', mode_name)
    zf.write('README.txt', compress_typecompression)

print()
print_info('write_compression.zip')

На этот раз элемент архива сжат.

$ python3 zipfile_write_compression.py

creating archive
adding README.txt with compression mode deflated

README.txt
  Comment     : b''
  Modified    : 2016-08-07 13:31:24
  System      : Unix
  ZIP version : 20
  Compressed  : 65 bytes
  Uncompressed: 76 bytes

Использование альтернативных имен членов архива

Передайте значение arcname в write () , чтобы добавить файл в архив с именем, отличным от исходного имени файла.

zipfile_write_arcname.py

from zipfile_infolist import print_info
import zipfile

with zipfile.ZipFile('write_arcname.zip', mode'w') as zf:
    zf.write('README.txt', arcname'NOT_README.txt')

print_info('write_arcname.zip')

В архиве нет никаких признаков оригинального имени файла.

$ python3 zipfile_write_arcname.py

NOT_README.txt
  Comment     : b''
  Modified    : 2016-08-07 13:31:24
  System      : Unix
  ZIP version : 20
  Compressed  : 76 bytes
  Uncompressed: 76 bytes

Запись данных из источников, отличных от файлов

Иногда необходимо записать в ZIP-архив данные, полученные не из существующего файла. Вместо того, чтобы записывать данные в файл, а затем добавлять этот файл в ZIP-архив, используйте метод writerestr () , чтобы напрямую добавить строку байтов в архив.

zipfile_writestr.py

from zipfile_infolist import print_info
import zipfile

msg  'This data did not exist in a file.'
with zipfile.ZipFile('writestr.zip',
                     mode'w',
                     compressionzipfile.ZIP_DEFLATED,
                     ) as zf:
    zf.writestr('from_string.txt', msg)

print_info('writestr.zip')

with zipfile.ZipFile('writestr.zip', 'r') as zf:
    print(zf.read('from_string.txt'))

В этом случае аргумент compress_type для ZipFile использовался для сжатия данных, поскольку writestr () не принимает аргумент для указания сжатия .

$ python3 zipfile_writestr.py

from_string.txt
  Comment     : b''
  Modified    : 2016-12-29 12:14:42
  System      : Unix
  ZIP version : 20
  Compressed  : 36 bytes
  Uncompressed: 34 bytes

b'This data did not exist in a file.'

Запись с помощью экземпляра ZipInfo

Обычно дата модификации вычисляется при добавлении файла или строки в архив. Экземпляр ZipInfo можно передать в Writestr () для определения даты изменения и других метаданных.

zipfile_writestr_zipinfo.py

import time
import zipfile
from zipfile_infolist import print_info

msg  b'This data did not exist in a file.'

with zipfile.ZipFile('writestr_zipinfo.zip',
                     mode'w',
                     ) as zf:
    info  zipfile.ZipInfo('from_string.txt',
                           date_timetime.localtime(time.time()),
                           )
    info.compress_type  zipfile.ZIP_DEFLATED
    info.comment  b'Remarks go here'
    info.create_system  0
    zf.writestr(info, msg)

print_info('writestr_zipinfo.zip')

В этом примере измененное время устанавливается на текущее время, данные сжимаются, и используется значение false для create_system . С новым файлом также связан простой комментарий.

$ python3 zipfile_writestr_zipinfo.py

from_string.txt
  Comment     : b'Remarks go here'
  Modified    : 2016-12-29 12:14:42
  System      : Windows
  ZIP version : 20
  Compressed  : 36 bytes
  Uncompressed: 34 bytes

Добавление к файлам

Помимо создания новых архивов, можно добавить к существующему архиву или добавить архив в конец существующего файла (например, файл .exe для самораспаковывающегося архива). Чтобы открыть файл для добавления к нему, используйте режим 'a' .

zipfile_append.py

from zipfile_infolist import print_info
import zipfile

print('creating archive')
with zipfile.ZipFile('append.zip', mode'w') as zf:
    zf.write('README.txt')

print()
print_info('append.zip')

print('appending to the archive')
with zipfile.ZipFile('append.zip', mode'a') as zf:
    zf.write('README.txt', arcname'README2.txt')

print()
print_info('append.zip')

Результирующий архив содержит два члена:

$ python3 zipfile_append.py

creating archive

README.txt
  Comment     : b''
  Modified    : 2016-08-07 13:31:24
  System      : Unix
  ZIP version : 20
  Compressed  : 76 bytes
  Uncompressed: 76 bytes

appending to the archive

README.txt
  Comment     : b''
  Modified    : 2016-08-07 13:31:24
  System      : Unix
  ZIP version : 20
  Compressed  : 76 bytes
  Uncompressed: 76 bytes

README2.txt
  Comment     : b''
  Modified    : 2016-08-07 13:31:24
  System      : Unix
  ZIP version : 20
  Compressed  : 76 bytes
  Uncompressed: 76 bytes

ZIP-архивы Python

Python может импортировать модули из ZIP-архивов с помощью zipimport, если эти архивы находятся в sys.path . Класс PyZipFile можно использовать для создания модуля, подходящего для использования таким образом. Дополнительный метод writepy () указывает PyZipFile сканировать каталог на наличие файлов .py и добавлять соответствующие .pyo или файл .pyc в архив. Если ни одна скомпилированная форма не существует, создается и добавляется файл .pyc .

zipfile_pyzipfile.py

import sys
import zipfile

if __name__  '__main__':
    with zipfile.PyZipFile('pyzipfile.zip', mode'w') as zf:
        zf.debug  3
        print('Adding python files')
        zf.writepy('.')
    for name in zf.namelist():
        print(name)

    print()
    sys.path.insert(0, 'pyzipfile.zip')
    import zipfile_pyzipfile
    print('Imported from:', zipfile_pyzipfile.__file__)

Если для атрибута отладки PyZipFile задано значение 3 , включена подробная отладка и вывод производится по мере компиляции каждого найденного файла .py .

$ python3 zipfile_pyzipfile.py

Adding python files
Adding files from directory .
Compiling ./zipfile_append.py
Adding zipfile_append.pyc
Compiling ./zipfile_getinfo.py
Adding zipfile_getinfo.pyc
Compiling ./zipfile_infolist.py
Adding zipfile_infolist.pyc
Compiling ./zipfile_is_zipfile.py
Adding zipfile_is_zipfile.pyc
Compiling ./zipfile_namelist.py
Adding zipfile_namelist.pyc
Compiling ./zipfile_printdir.py
Adding zipfile_printdir.pyc
Compiling ./zipfile_pyzipfile.py
Adding zipfile_pyzipfile.pyc
Compiling ./zipfile_read.py
Adding zipfile_read.pyc
Compiling ./zipfile_write.py
Adding zipfile_write.pyc
Compiling ./zipfile_write_arcname.py
Adding zipfile_write_arcname.pyc
Compiling ./zipfile_write_compression.py
Adding zipfile_write_compression.pyc
Compiling ./zipfile_writestr.py
Adding zipfile_writestr.pyc
Compiling ./zipfile_writestr_zipinfo.py
Adding zipfile_writestr_zipinfo.pyc
zipfile_append.pyc
zipfile_getinfo.pyc
zipfile_infolist.pyc
zipfile_is_zipfile.pyc
zipfile_namelist.pyc
zipfile_printdir.pyc
zipfile_pyzipfile.pyc
zipfile_read.pyc
zipfile_write.pyc
zipfile_write_arcname.pyc
zipfile_write_compression.pyc
zipfile_writestr.pyc
zipfile_writestr_zipinfo.pyc

Imported from: pyzipfile.zip/zipfile_pyzipfile.pyc

Ограничения

Модуль zipfile не поддерживает файлы ZIP с добавленными комментариями или многодисковые архивы. Он поддерживает файлы ZIP размером более 4 ГБ, в которых используются расширения ZIP64.

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