【python基础】—zipfile模块ZipFile类的使用


前言

本文主要介绍zipfile模块下ZipFile类的使用方法,包括对现在zip文件中读取及创建。


一、ZIP文件的读取

1、ZipFile类的四种模式

zipfile模块中的ZipFile类的使用方法类似 Python 内置的 open() 函数,允许使用不同的模式打开 ZIP文件,如下:

模式描述
r读取模式
w写入模式
a追加模式
x独占模式
  • 读取模式

示例:打开"test.zip"文件。

import zipfile
with zipfile.ZipFile("test.zip",mode="r") as files:
	files.printdir()

在这里插入图片描述

ZipFile()初始化的第一个参数可以是一个字符串,表示需要打开的ZIP文件的路径,这个参数也可以接受文件对象和路径类对象。
ZipFile()初始化的第二个参数是一个单字母的字符串,表示用于打开文件模式。

printdir()函数提供了一种在屏幕上显示底层ZIP文件内容的快捷方法。其输出的易读的表格形式,有三列信息:

  • File Name
  • Modified
  • Size
  • 写入模式

示例:将"hello.txt"添加到"test.zip"中,可以使用写入模式(“w”)。

import zipfile
with zipfile.ZipFile("test.zip","w") as files:
	files.write("hello.txt")
	files.printdir()

在这里插入图片描述

从上面的例子可以看到使用"w"模式写入"hello.txt"文件覆盖了原有的内容。

注意: 使用"w"模式写入内容,如果目标ZIP文件存在,会覆盖原有的内容,重新写入新的内容,有可能导致原有的内容丢失,要小心使用。如果目标ZIP文件不存在,会自动创建一个zip文件,并写入要传入的内容。

  • 追加模式

追加模式(“a”)允许将新的文件内容追加到现在的目标ZIP文件中,不会覆盖原有的文件,因此原有的内容是安全的。如果目标ZIP文件不存在,则"a"模式会为您创建一个新文件,然后追加作为参数,传入.write()的任何文件。

示例:往"test.zip"目标文件中追加"new_hello.txt"。

import zipfile
with zipfile.ZipFile("test.zip","a") as files:
	files.write("new_hello.txt")
	files.printdir()

在这里插入图片描述

  • 独占模式

独占模式(“x”)允许您独占地创建新的ZIP文件并将新的成员文件写入其中,当想要创建一个新的ZIP文件而不覆盖现有文件时,使用独占模式。

示例:往已经存在"test.zip"目标文件中追加"article.txt"。

import zipfile
with zipfile.ZipFile("test.zip","x") as files:
	files.write("article.txt")

在这里插入图片描述
注意: 如果目标ZIP文件已存在,则会得到FileExistsError。

2、解析ZIP文件中的元数据

方法描述
.getinfo(filename)返回一个关于filename提供的成员文件的信息的ZipInfo对象,注意filename必须包含底层zip文件中目标文件的路径。
.infolist()返回一个ZipInfo对象列表,每个文件占一项。
.namelist()返回一个包含底层归档中所有成员文件名的列表。该列表中的名称是.getinfo()的有效参数。
  • getinfo(filename)

示例:返回"test.zip"关于"hello.txt"成员文件的ZipInfo对象。

import zipfile

with zipfile.ZipFile("test.zip","r") as files:
	info = files.getinfo("hello.txt")
	
info.filename #'hello.txt'
info.file_size #11
info.compress_size # 11
info.date_time # (2023, 11, 30, 16, 51, 0)

getinfo(filename)将成员文件作为参数,并返回一个包含其信息的ZipInfo对象。

ZipInfo对象有一些属性:
ZipInfo.filename:文件名。
ZipInfo.file_size:未压缩原始文件的大小(以字节为单位)。
ZipInfo.compress_size:压缩原始文件的大小(以字节为单位)。
ZipInfo.date_time:最后修改日期。

注意: 默认情况下,ZipFile 不会压缩输入文件以将其添加到最终归档中。这就是上例中 file_size 和 compress_size 大小相同的原因。

  • infolist()

示例:返回"test.zip"的所有成员文件的ZipInfo对象列表。

import zipfile
import datetime

with zipfile.ZipFile("test.zip","r") as files:
	for info in files.infolist():
		print(f"Filename:{info.filename}")
		print(f"Modified:{datetime.datetime(*info.date_time)}")
		print(f"Normal size:{info.file_size} bytes")
		print(f"Compress size:{info.compress_size} bytes")
		print("-"*20)

# 输出:
# Filename:hello.txt
# Modified:2023-11-30 16:51:00
# Normal size:11 bytes
# Compress size:11 bytes
# --------------------
# Filename:new_hello.txt
# Modified:2023-11-30 16:55:00
# Normal size:51 bytes
# Compress size:51 bytes
# --------------------

for循环迭代来自.infolist()的ZipInfo对象,检索文件名,最后修改日期、未压缩大小以及每个成员文件的压缩后大小。

  • namelist()

示例1:快速检查并列出成员文件的名称。

import zipfile
with zipfile.ZipFile("test.zip","r") as files:
	for filename in files.namelist():
		print(filename)

# 输出:
# hello.txt
# new_hello.txt

示例2:有一个zip文件,其中包含了不同类型的成员文件(.docx、.xlsx、.txt等)。我们只需要获取有关.txt文件的信息,并且不需要获取完整信息,只需要提取.date_time,即最后修改日期。可以先按扩展名过滤文件并仅在.docx文件上调用.getinfo()。

import os
import fnmatch
import zipfile
 
matches = []
extension="txt"
with zipfile.ZipFile("test.zip","r") as files:
    for filename in files.namelist():
        if fnmatch.fnmatch(filename , f"*.{extension}"):
            matches.append(filename)
            info = files.getinfo(filename)
            print(info.date_time)
print(matches)

# 输出:
# hello.txt的最后创建时间: (2023, 11, 30, 16, 51, 0)
# new_hello.txt的最后创建时间: (2023, 11, 30, 16, 55, 0)
# ['hello.txt', 'new_hello.txt']

3、解析ZIP文件中的内容

  • .read()

使用.read(),需要使用读取(“r”)或者追加(“w”)模式打开ZIP文件,注意,.read()以字节流的形式返回目标文件的内容。

示例1:

import zipfile
with zipfile.ZipFile("test.zip","r") as files:
	for line in files.read("new_hello.txt").split(b"\r\n"):
		print(line)

在这里插入图片描述

注意:.read()以字节流形式返回目标文件的内容,在些示例中,以换行符"\r\n"作为分隔符,使用.split()将流拆分为行。因为.split()正在一个字节对象上进行操作,所以需要在字符串前添加前导b作为参数。

示例2:

import zipfile
with zipfile.ZipFile("test.zip","r") as files:
	for line in files.read("new_hello.txt",pwd=b"test").split(b"\n"):
		print(line)

ZipFile.read()还接受名为pwd的第二个位置参数。提供密码 test 来读取加密文件。pwd参数接受字节类型的值。
如果在未提供所需密码的情况下对加密文件使用.read(),则会报错RuntimeError: File 'new_hello.txt' is encrypted, password required for extraction

注意: Python 的 zipfile 支持解密。但是它不支持创建加密 ZIP 文件。这就是需要使用外部文件归档工具来加密文件的原因。

示例3:ZipFile.setpassword()

import zipfile

with zipfile.ZipFile() as files:
	files.setpassword(b'abc')
	for file in files.namelist():
		print(file)
		print("-"*20)
		for line in files.read(file).split(b"\n"):
			print(line)

使用 .setpassword() 只需要提供一次密码,ZipFile 使用该唯一密码来解密所有成员文件。这种访求可以避免每次调用.read()或其他接受pwd参数的方法时提供密码。但如果ZIP文件每个成员文件的密码不一样的的话,就需要使用 .read() 和 pwd 参数为每个成员文件提供特定的密码。

注意: 当使用 pwd 参数时,会覆盖已经通过的 .setpassword() 设置的任何归档级密码。

  • .open()

示例1:.open()与"r"模式组合使用。

import zipfile

with zipfile.ZipFile("test.zip","r") as files:
	with file.open("new_hello.txt","r") as hello:
		for line in hello:
			print(line)

示例2:.open()与"w"模式组合使用。

以追加模式打开test.zip,然后通过"w"模式调.open()的创建"new_world.txt"。返回一个支持.write()的类文件对象,允许将字节写入新创建的文件。

import zipfile

with zipfile.ZipFile("test.zip","a") as files:
	with files.open("new_world.txt","w") as new_world:
		new_world.write(b"welcome to world!")

with zipfile.ZipFile("test.zip","r") as files:
	files.printdir()
	print("-"*20)
	files.read("new_world.txt")

在些示例中,将 b"welcome to world!" 写入"new_world.txt"。当执行流退出内部的with语句时,Python将输入字节写入成员文件。当外部的with语句退出时,Python会将"new_hello.txt"写入底层ZIP文件"test.zip"。

注意: 需要为.open()提供一个非现有的文件名,如果使用底层归档中已经存在的文件名,那么会最终得到一个重复的文件和一个UserWarning异常。

  • 读取文本内容

.read()和.write()这两种方法都适用于字节。

方法一:bytes.decode()

ZipFile.read() 以字节形式返回目标成员文件的内容,故 .decode() 可以直接对这些字节进行操作。.decode() 方法使用给定的字符编码格式将 bytes 对象解码为字符串。

import zipfile
with zipfile.ZipFile("test.zip","r") as files:
	text = files.read("new_hello.txt").decode("utf-8")
	
print(text)

在这里插入图片描述

在此示例中,将"hello.txt"的内容作为字节读取。然后调用 .decode() 来将字节解码为使用 UTF-8 作为编码的字符串。

注意: 简体中文读者,最常见的一个乱码错误是,中文文本文件使用 Windows 默认的 GB2312 编码保存,而此处被以默认的 UTF-8 解码。因此见到许多“烫烫烫的锟斤拷”时不必惊慌,指定使用 GB2312 再尝试读取一次,很可能便解决了问题。

方法二:io.TextIOWrapper()

示例:使用 io.TextIOWrapper() 将"new_hello.txt"成员文件作为文字流读取。

import io
import zipfile
with zipfile.ZipFile("test.zip","r") as files:
	with files.open("new_hello.txt","r") as hello:
		for line in io.TextIOWrapper(hello,"utf-8"):
			print(line.strip())

在这里插入图片描述

在些示例中,内层with语句从"test.zip"归档中打开成员文件"new_hello.txt",然后生成的二进制类文件对象hello作为参数传递给io.TextIOWrapper。通过使用UTF-8字符编码格式解码hello内容来创建有缓冲的文字流。因此,可以直接从目标成员文件中获得文字流。

4、读取ZIP文件下的成员文件

  • .extract() 一次提取一个文件。

此方法接受一个成员文件的名称,并将其提取到由 path 指定的目录。目标 path 默认为当前目录:

import zipfile
with zipfile.ZipFile("test.zip","r") as files:
    files.extract("hello.txt",path=".//test") 

在些示例中,现在"hello.txt"将会出现在 .//test 目录中。
如果目标文件名已存在于输出目录中,则 .extract() 将不经请求确认直接覆盖它。
如果输出目录不存在,则 .extract() 会为您创建它。注意 .extract() 返回提取文件的路径。

示例:提取指定类型文件。比如 “test.zip” 文件下有两个 .txt 文件和一个 .py 文件。现在只提取 .txt 文件。

import zipfile

with zipfile.ZipFile("test.zip","r") as files:
	for file in files.namelist():
		if file.endswith(".txt"):
			files.extract(file,".//extract_test")

with 语句打开 “test.zip” 以供读取。循环使用 namelist() 遍历归档中的每个文件,而条件语句检查文件名是否以 .txt 扩展名结尾。如果是,则使用 .extract() 将手头的文件解压到目标目录 .//extract_test。

  • .extractall() 一次提取所有文件
import zipfile
with zipfile.ZipFile("test.zip","r") as files:
	files.extractall(".//test")

“test.zip” 中的所有当前内容将出现在./ 目录中。
如果将一个不存在的目录传递给 .extractall(),那么该方法会自动创建这个目录。
如果目标目录中已经存在任何成员文件,那么 .extractall() 将不经请求确认直接覆盖掉它们,所以要小心。
如果只需要从给定归档中提取一些成员文件,则可以使用 members 参数。此参数接受成员文件列表,即手头归档中全部文件列表的子集。

  • .extract() 与 .extractall() 方法一样,都用于加密文件。在这种情况下,需要提供所需的 pwd 参数或使用 .setpassword() 设置归档级密码。

示例1:

import zipfile

with zipfile.ZipFile("test.zip","r") as files:
	for file in files.namelist():
		if file.endswith(".txt"):
			files.extract(file,".//extract_test",pwd=b"abc")
import zipfile

with zipfile.ZipFile("test.zip","r") as files:
	files.setpassword(b"abc")
	for file in files.namelist():
		if file.endswith(".txt"):
			files.extract(file,".//extract_test")

示例2:

import zipfile
with zipfile.ZipFile("test.zip","r") as files:
	files.extractall(".//test",pwd=b"abc")
import zipfile
with zipfile.ZipFile("test.zip","r") as files:
	files.setpassword(b"abc")
	files.extractall(".//test")

5、确保打开的是有效的ZIP文件

  • try…expect…
import zipfile

try:
	with zipfile.ZipFile("test.zip") as files:
		files.printdir()
except zipfile.BadZipFile as error:
	print(error)

如果示例中可以成功打开"test.zip"文件而不引发BadZipFile异常。test.zip是有效的ZIP格式;如果无法成功打开test.zip文件,那么该文件不是有效的ZIP文件。

  • is_zipfile()

该函数接受一个保存文件系统中ZIP文件路径的filename参数,此参数可以接受字符串,类文件或路径对象。
如果filename是有效的ZIP文件,则该函数返回True,否则返回False。

import zipfile

if zipfile.is_zipfile("test.zip"):
	with zipfile.ZipFile("test.zip","r") as files:
		files.printdirt()
else:
	print("File is not a zip file")

6、使用后关闭ZIP文件

  • close()
import zipfile
with zipfile.ZipFile("test.zip","r") as files:
	files.printdir()
files.close()

二、ZIP文件的创建

1、从多个常规文件创建ZIP文件

  • 将多个相关的文件创建ZIP归档。

示例1:

import zipfile

filenames=["article.txt","movies.txt"]
with zipfile.ZipFile("multiple_files.zip","w") as zip_files:
	for filename in filenames:
		zip_files.write(zip_files)

for()循环遍历输入的文件列表,并使用.write()将它们写入底层文件ZIP文件,一旦流程退出 with 语句,ZipFile 会自动关闭归档,保存更改。现在获得一个包含原始文件列表中所有文件的 multiple_files.zip 归档。

2、从目录创建ZIP文件

  • 将一个目录中的内容打包到ZIP文件中归档。

示例1:目录中不包括子目录。在 .//test 中,只有三个普通文件。

import pathlib
import zipfile

directory = pathlib.Path(".//test") #从源目录中创建一个pathlib.Path对象

with zipfile.ZipFile("directory_tree.zip","w") as zip_files:
    for file_path in directory.iterdir(): #pathlib.Path对象对iterdir()的调用会返回一个遍历底层目录中条目的迭代器
        zip_files.write(file_path,arcname=file_path.name)    
        
with zipfile.ZipFile("directory_tree.zip","r") as zip_files:
    zip_files.printdir()

在这里插入图片描述

在此示例中,
使用 pathlib.Path.iterdir() 递归遍历 .//test 下条目,然后将每个文件写入目录ZIP归档。
使用 file_path.name 传递给 .write() 的第二个参数。此参数名为 arcname,保存着生成的归档中成员文件的名称。
注意: 如果不将 file_path.name 传递给 arcname ,那么默认源目录将作为ZIP文件的源目录。

示例2:目录下有普通文件和一个包含文件的子目录。在 .//test 中,包含有三个普通文件和一个包含2个文件的子目录test_1。

import pathlib
import zipfile

directory = pathlib.Path(".//test") #从源目录中创建一个pathlib.Path对象

with zipfile.ZipFile("directory_tree.zip","w") as zip_files:
	for file_path in directory.rglob("*"): #使用pathlib.Path.rglob()递归遍历 .//test 下的目录树
		zip_files.write(file_path,arcname=file_path.relative_to(directory))

with zipfile.ZipFile("directory_tree.zip","r") as zip_files:
    zip_files.printdir()

在这里插入图片描述

在此示例中,
使用 pathlib.Path.rglob() 递归遍历 .//test 下的目录树,然后将每个文件和子目录写入目录ZIP归档。
使用 pathlib.Path.relative_to() 来获取每个文件的相对路径,然后将其结果传递给.write()的第二个参数 arcname ,这样生成的 ZIP文件 最终具有与源目录相同的内部结构,注意: 如果你希望源上当作为ZIP文件的根目录,则可以去掉这个参数。

3、压缩文件和目录

compression 方法是 ZipFile 初始化方法的第三个参数,如果想在 ZIP文件 创建时压缩文件,则可以将此参数设置为以下常量之一:

常量压缩方法所需模块
zipfile.ZIP_DEFLATEDDeflatezlib
zipfile.ZIP_BZIP2Bzip2bz2
zipfile.ZIP_LZMALZMAlzma

在压缩文件时,另一个与 ZipFile 相关参数是 compresslevel。些参数控制使用的压缩级别,使用 Deflate 方法,compresslevel 可以取从 0 到 9 的整数。使用 Bzip2 方法,可以传递从 1 到 9 的整数。在这两种情况下,当压缩级别增加时,压缩率会更高,压缩速度会更慢。

示例1:使用 Deflate 方法来归档和压缩给定目录的内容(最常用的方法)。

import pathlib
from zipfile import ZipFile,ZIP_DEFLATED

directory = pathlib.Path(".//test")

with zipfile.ZipFile("directory_new.zip","w",ZIP_DEFLATED,compresslevel=9) as zip_files:
	for file_path in directory.rglob("*"):
		zip_files.write(file_path,arcname=file_path.relative_to(directory))

在些示例中,将9传递给copresslevel以获得最大压缩。提供此参数时要使用关键字参数,这是因为compresslevel 并非 ZipFile 初始化时的第四个位置参数。

运行此代码后,在当前目录中会出现一个"directory_new.zip"文件,你会发现这个文件的大小与原始的directory_tree.zip相比较,有显著减小。
在这里插入图片描述

示例2:压缩的.//test 目录下的文件大于 4 GB 。

import pathlib
from zipfile import ZipFile,ZIP_DEFLATED

directory = pathlib.Path(".//test")

with zipfile.ZipFile("data.zip","w",ZIP_DEFLATED,compresslevel=9) as zip_files:
	for file_path in directory.rglob("*"):
		zip_files.write(file_path,arcname=file_path.relative_to(directory))

报错:RuntimeError: File size unexpectedly exceeded ZIP64 limit
原因:

ZipFile 的初始化时,接受名为 allowZip64 的第四个参数。这是一个布尔参数,告诉 ZipFile 为大于 4 GB 的文件使用 .zip64 扩展。默认情况下为False。

解决方案:传入 allowZip64=True 参数来创建 ZIP64 格式的压缩文件。

import pathlib
from zipfile import ZipFile,ZIP_DEFLATED

directory = pathlib.Path(".//test")

with zipfile.ZipFile("data.zip","w",ZIP_DEFLATED,compresslevel=9,allowZip64=True) as zip_files:
	for file_path in directory.rglob("*"):
		zip_files.write(file_path,arcname=file_path.relative_to(directory))

4、依次创建ZIP文件

示例:要创建一个包含或不包含内容的初始 ZIP 文件,然后在新成员文件可用时立即追加它们。

import zipfile

def append_member(zip_file,member):
	with zipfile.ZipFile(zip_file,mode="a") as zip_files:
		zip_files.write(member)

def get_file_from_stream():
	for file in files:
		yield file

files=["hello.txt","movies.txt","new_hello.txt"]
for filename in get_file_from_stream():
	append_member("incremental.zip",filename)
	
with zipfile.ZipFile("incremental.zip",mode="r") as zip_files:
	zip_files.printdir()

在此示例中,append_member() 函数将文件(member)追加到输入 ZIP归档(zip_file),该函数会在每次被调用时打开和关闭目标归档。
get_file_from_stream() 函数是一个生成器函数,用于模拟处理的文件流。
最后,for循环调用 append_member() 函数,将成员文件依次添加到 incremental.zip中。

三、使用命令行运行zipfile

使用方法:

Usage:
    zipfile.py -l zipfile.zip        # Show listing of a zipfile
    zipfile.py -t zipfile.zip        # Test if a zipfile is valid
    zipfile.py -e zipfile.zip target # Extract zipfile into target dir
    zipfile.py -c zipfile.zip src ... # Create zipfile from sources
  • 1、查看ZIP文件的成员文件信息

语法:python -m zipfile -l ZIP文件名

示例:

/data1/iap/dingji/Python-3.6.5/python -m zipfile -l data.zip

运行结果:

File Name                                             Modified             Size
data_cj_2024020_20240215.csv                       2024-02-16 19:07:36      2548251
data_high_2024020_20240215.csv                       2024-02-16 19:07:42        32446

从示例中, 可以看出,和zipinfo对象的printdir()的输出结果一样。

  • 2、判断是否为有效的ZIP文件

语法:python -m zipfile -t ZIP文件名

示例1:data.zip文件存在。

python -m zipfile -t data.zip

运行结果:

Done testing
  • 示例2:data_1.zip文件不存在。
python -m zipfile -l data_1.zip

运行结果:

FileNotFoundError: [Errno 2] No such file or directory: 'data_1.zip'
  • 3、提取ZIP成员文件到目标路径下面

语法:python -m zipfile -e ZIP文件名 新的路径

示例:

python -m zipfile -e data.zip  .//test

运行此命令后,工作目录中将有一个新的 .//test 文件夹,里面包含了data.zip 的所有成员文件。

  • 4、创建ZIP文件

语法:python -m zipfile -c 新建ZIP文件名 成员文件1 成员文件2 …

示例:

python -m zipfile -c new_data.zip data_cj_2024020_20240215.csv data_high_2024020_20240215.csv

此命令创建一个 new_data.zip 文件,其中包含data_cj_2024020_20240215.csv、data_high_2024020_20240215.csv文件。


参考文章:
https://zhuanlan.zhihu.com/p/480329034

  • 28
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值