9.6. zipfile — 访问 zip 压缩文件 | 数据压缩和归档 |《python 3 标准库实例教程》| python 技术论坛-江南app体育官方入口

未匹配的标注

目的:读取和写入 zip 存档文件。

zipfile 模块可用于操作 zip 存档文件,.zip 是 pc 程序 pkzip 推广的格式。

为了演示用,你需要创建以下三个文件

  1. readme.txt 内容如下,注意最后一行为空行
    the examples for the zipfile module use 
    this file and example.zip as data.
  2. 使用压缩软件将 readme.txt 压缩为 example.zip,压缩式选储存选项,不需要压缩
  3. 新建一个空的文本文档,并改名为 bad_example.zip

测试 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_is_zipfile.py 中使用的是相对路径,你需要在同一个目录/路径下放置测试文件以及 python 脚本,并在这个路径下使用 python 解释器执行脚本,才能得到正确的输出。否则你需要在 python 脚本中使用绝对路径。下述所有的例子均需要放置在同一级目录下。

从 zip 存档中读取元数据

使用 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']

存档中的文件名列表只是存档中可用信息的一小部分。可以使用 infolist() 或 getinfo() 方法访问 zip 文件的全部元数据。

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

除了以上输出信息之外,zip 存档还包含额外的数据。不过你需要仔细阅读  中的 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

如果你已经预先知道了 zip 存档中某个文件的名字。你可以通过 getinfo() 方法直接得到它的 zipinfo 对象。

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

从 zip 存档中读取文件

要访问存档中的数据,可以将文件名传递给 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

创建新存档

要创建新的 zip 存档,需要以 'w' 模式实例化一个 zipfile 对象,存档中任何现有的文件都会被清空,相当于新建一个存档开始写入。如果你想要向现有存档中添加文件,你可以使用 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

要实现压缩功能,需要使用 模块。如果 模块可用,你可以使用 zipfile.zip_deflated 选项,让 zipfile 进入对单独的文件或整个存档进行压缩的模式。默认的压缩设置是  zipfile.zip_stored,这种模式下 zipfile 只会将文件添加进存档而不压缩它。

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_type=compression)
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

使用其它的存档成员名

给 write() 方法传递一个 arcname 值,你就可以将文件以新的文件名加入存档。等价于将文件改名后再加入存档。

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 存档,而不是将现有的文件加入存档。你不需要先把字符串写入文件,然后再将文件写入存档,通过 writestr() 方法,你可以直接将字节流的字符串写入 zip 存档。

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',
                     compression=zipfile.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'))

以上示例中,传递给 zipfile 的 compress_type 参数指定了需要压缩数据,因为 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_time=time.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')

在这个例子里,修改时间定为当前时间、数据被压缩了、使用了一个假的 create_system 值「原作者使用 unix 系统,对应的 create_system 值为 3,而这里设置为 0 对应 windows 系统」并加上了一条简单的注释。

$ 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

python zip 存档

python 可以通过 从 sys.path 路径中的 zip 存档内导入模块。我们可以将已经编写好的
 pyzipfile 类构造成一个可以这样使用的模块。额外的  writepy() 方法告诉 pyzipfile 扫描当前目录下的每一个 .py 文件,并将对应的 .pyo 或 .pyc 文件添加进 zip 存档。如果两种格式的文件都不存在,新的  .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 文件,也不支持分卷压缩文件。通过 zip64 扩展它可以支持大于 4gb 的 zip 文件。

参见

  •  -- zip 压缩库
  •  -- 读写 tai 存档
  •  -- 从 zip 存档中导入 python 库
  •  -- zip 存档文件格式的官方说明

本文章首发在 江南app体育官方入口 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 cc 协议,如果我们的工作有侵犯到您的权益,请及时联系江南app体育官方入口。

原文地址:https://learnku.com/docs/pymotw/zipfile-...

译文地址:https://learnku.com/docs/pymotw/zipfile-...

上一篇 下一篇
讨论数量: 0



暂无话题~
网站地图