课程
处理 Zip 文件的先决条件
- 您需要了解 Python 的文件处理,才能理解 Zip 文件的处理。如果您还不熟悉文件处理,请前往 W3Schools 的 File Handling 部分学习。
- Python 的面向对象(OOP)概念
- Python 基础概念,如条件语句、循环、函数、类等
- 如果您不了解 Python,可以学习 DataCamp 的免费课程 Intro to Python for Data Science,或阅读 Python 官方的文档。
打开这个 链接 以下载我在后续章节中使用的所有 Zip 文件夹。
什么是 Zip 文件?
Zip 是一种支持无损数据压缩的归档文件格式。Zip 文件是一个包含一个或多个压缩文件的单一文件。
Zip 文件的用途?
- Zip 文件可以把所有相关文件放在一个地方。
- Zip 文件有助于减小数据体积。
- 在许多连接下,Zip 文件的传输速度比单个文件更快。
zipfile 模块
使用 dir() 方法探索 zipfile 模块的所有方法和类。查看下面的代码以获取该模块的所有类和方法。
import zipfile # importing the 'zipfile' module
print(dir(zipfile))
['BZIP2_VERSION', 'BadZipFile', 'BadZipfile', 'DEFAULT_VERSION', 'LZMACompressor', 'LZMADecompressor', 'LZMA_VERSION', 'LargeZipFile', 'MAX_EXTRACT_VERSION', 'PyZipFile', 'ZIP64_LIMIT', 'ZIP64_VERSION', 'ZIP_BZIP2', 'ZIP_DEFLATED', 'ZIP_FILECOUNT_LIMIT', 'ZIP_LZMA', 'ZIP_MAX_COMMENT', 'ZIP_STORED', 'ZipExtFile', 'ZipFile', 'ZipInfo', '_CD64_CREATE_VERSION', '_CD64_DIRECTORY_RECSIZE', '_CD64_DIRECTORY_SIZE', '_CD64_DISK_NUMBER', '_CD64_DISK_NUMBER_START', '_CD64_EXTRACT_VERSION', '_CD64_NUMBER_ENTRIES_THIS_DISK', '_CD64_NUMBER_ENTRIES_TOTAL', '_CD64_OFFSET_START_CENTDIR', '_CD64_SIGNATURE', '_CD_COMMENT_LENGTH', '_CD_COMPRESSED_SIZE', '_CD_COMPRESS_TYPE', '_CD_CRC', '_CD_CREATE_SYSTEM', '_CD_CREATE_VERSION', '_CD_DATE', '_CD_DISK_NUMBER_START', '_CD_EXTERNAL_FILE_ATTRIBUTES', '_CD_EXTRACT_SYSTEM', '_CD_EXTRACT_VERSION', '_CD_EXTRA_FIELD_LENGTH', '_CD_FILENAME_LENGTH', '_CD_FLAG_BITS', '_CD_INTERNAL_FILE_ATTRIBUTES', '_CD_LOCAL_HEADER_OFFSET', '_CD_SIGNATURE', '_CD_TIME', '_CD_UNCOMPRESSED_SIZE', '_ECD_COMMENT', '_ECD_COMMENT_SIZE', '_ECD_DISK_NUMBER', '_ECD_DISK_START', '_ECD_ENTRIES_THIS_DISK', '_ECD_ENTRIES_TOTAL', '_ECD_LOCATION', '_ECD_OFFSET', '_ECD_SIGNATURE', '_ECD_SIZE', '_EndRecData', '_EndRecData64', '_FH_COMPRESSED_SIZE', '_FH_COMPRESSION_METHOD', '_FH_CRC', '_FH_EXTRACT_SYSTEM', '_FH_EXTRACT_VERSION', '_FH_EXTRA_FIELD_LENGTH', '_FH_FILENAME_LENGTH', '_FH_GENERAL_PURPOSE_FLAG_BITS', '_FH_LAST_MOD_DATE', '_FH_LAST_MOD_TIME', '_FH_SIGNATURE', '_FH_UNCOMPRESSED_SIZE', '_SharedFile', '_Tellable', '_ZipDecrypter', '_ZipWriteFile', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_check_compression', '_check_zipfile', '_get_compressor', '_get_decompressor', 'binascii', 'bz2', 'compressor_names', 'crc32', 'error', 'importlib', 'io', 'is_zipfile', 'lzma', 'main', 'os', 're', 'shutil', 'sizeCentralDir', 'sizeEndCentDir', 'sizeEndCentDir64', 'sizeEndCentDir64Locator', 'sizeFileHeader', 'stat', 'stringCentralDir', 'stringEndArchive', 'stringEndArchive64', 'stringEndArchive64Locator', 'stringFileHeader', 'struct', 'structCentralDir', 'structEndArchive', 'structEndArchive64', 'structEndArchive64Locator', 'structFileHeader', 'sys', 'threading', 'time', 'zlib']
您看到了一堆类和方法,但并不需要全部掌握。本文只会介绍处理Zip 文件时常用的一些类和方法。
下面简要介绍一些有用的异常、类和方法。
异常
异常用于按需显示具体的错误信息。在 Python 中,您可以使用 try、except、finally 关键字进行错误处理。
如果您对错误处理不熟悉,请阅读 Python 官方文档的 Error Handling 章节。
来看一下 zipfile 模块中的所有异常。
zipfile.BadZipFile
zipfile.BadZipFile 是 zipfile 模块中的一个异常。当遇到损坏的 Zip 文件时会引发该错误。请看下面的示例。
## zipfile.BadZipFile
import zipfile
def main():
try:
with zipfile.ZipFile('sample_file.zip') as file: # opening the zip file using 'zipfile.ZipFile' class
print("Ok")
except zipfile.BadZipFile: # if the zip file has any errors then it prints the error message which you wrote under the 'except' block
print('Error: Zip file is corrupted')
if __name__ == '__main__': main()
## I used a badfile for the test
Ok
zipfile.LargeZipFile
如果您要处理大型 Zip 文件,需要在打开 Zip 时启用 ZIP64 功能。否则会引发 LargeZipFile。如下示例所示。
## zipfile.LargeZipFile
## Without enabling 'Zip64'
import zipfile
def main():
try:
with zipfile.ZipFile('sample_file.zip') as file:
print('File size is compatible')
except zipfile.LargeZipFile: # it raises an 'LargeZipFile' error because you didn't enable the 'Zip64'
print('Error: File size if too large')
if __name__ == '__main__': main()
File size is compatible
## zipfile.LargeZipFile
## With enabling 'ZIP64'
import zipfile
def main():
try:
with zipfile.ZipFile('sample_file.zip', mode = 'r', allowZip64 = True) as file: # here enabling the 'Zip64'
print('File size is compatible')
except zipfile.LargeZipFile:
print('Error: File size if too large') # if the file size is too large to open it prints the error you have written
if __name__ == '__main__': main()
File size is compatible
请选择适合做异常处理演示的 Zip 文件并尝试运行程序,您会更清楚地理解。
类
简单来说,类是一组方法和属性的集合。您可以通过创建类的实例,在需要的地方调用这些方法和属性。
下面来看 zipfile 模块中的一些类。
zipfile.ZipFile
用于处理Zip 文件最常见的类是 ZipFile。
zipfile.ZipFile 用于读写 Zip 文件。它包含若干用于处理 Zip 文件的方法。
现在使用 dir() 来探索 ZipFile 类的方法。参见代码。
import zipfile
print(dir(zipfile.ZipFile)) # accessing the 'ZipFile' class
['_RealGetContents', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_extract_member', '_fpclose', '_open_to_write', '_sanitize_windows_name', '_windows_illegal_name_trans_table', '_write_end_record', '_writecheck', 'close', 'comment', 'extract', 'extractall', 'fp', 'getinfo', 'infolist', 'namelist', 'open', 'printdir', 'read', 'setpassword', 'testzip', 'write', 'writestr']
您已经在前面的示例中使用过 zipfile.ZipFile 类来读取 Zip 文件。
zipfile.ZipFile 包含许多方法,如 extract、open、getinfo、setpassword 等,用于处理 Zip 文件。
我们来看一些 ZipFile 类的方法。
## zipfile.ZipFile
import zipfile
def main():
with zipfile.ZipFile('sample_file.zip') as file:
# ZipFile.infolist() returns a list containing all the members of an archive file
print(file.infolist())
# ZipFile.namelist() returns a list containing all the members with names of an archive file
print(file.namelist())
# ZipFile.getinfo(path = filepath) returns the information about a member of Zip file.
# It raises a KeyError if it doesn't contain the mentioned file
print(file.getinfo(file.namelist()[-1]))
# ZipFile.open(path = filepath, mode = mode_type, pwd = password) opens the members of an archive file
# 'pwd' is optional -> if it has password mention otherwise leave it
text_file = file.open(name = file.namelist()[-1], mode = 'r')
# 'read()' method of the file prints all the content of the file. You see this method in file handling.
print(text_file.read())
# You must close the file if you don't open a file using 'with' keyword
# 'close()' method is used to close the file
text_file.close()
# ZipFile.extractall(path = filepath, pwd = password) extracts all the files to current directory
file.extractall()
# after executing check the directory to see extracted files
if __name__ == '__main__': main()
[<ZipInfo filename='extra_file.txt' filemode='-rw-rw-rw-' file_size=59>, <ZipInfo filename='READ ME.txt' filemode='-rw-rw-rw-' file_size=59>, <ZipInfo filename='even_odd.py' filemode='-rw-rw-rw-' file_size=129>]
['extra_file.txt', 'READ ME.txt', 'even_odd.py']
<ZipInfo filename='even_odd.py' filemode='-rw-rw-rw-' file_size=129>
b"num = int(input('Enter a Number:- '))\r\nif num % 2 == 0:\r\n\tprint('{} is Even'.fromat(num))\r\nelse:\r\n\tprint('{} is Odd'.fromat(num))"
如果您想学习 ZipFile 类的全部方法,可以对想了解的方法使用 help() 函数。
或者访问 Python 官方的文档。
zipfile.ZipInfo
zipfile.ZipInfo 类用于表示 Zip 文件夹中的成员。
首先使用 dir() 方法探索 zipfile.ZipInfo 类的所有对象。见下方代码。
## zipfile.ZipInfo
import zipfile
print(dir(zipfile.ZipInfo))
['CRC', 'FileHeader', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '_decodeExtra', '_encodeFilenameFlags', '_raw_time', 'comment', 'compress_size', 'compress_type', 'create_system', 'create_version', 'date_time', 'external_attr', 'extra', 'extract_version', 'file_size', 'filename', 'flag_bits', 'from_file', 'header_offset', 'internal_attr', 'is_dir', 'orig_filename', 'reserved', 'volume']
下面来看 zipfile.ZipInfo 类的一些方法。
## zipfile.ZipInfo
import zipfile
def main():
with zipfile.ZipFile('sample_file.zip') as file:
# 'infolist()' is the object of 'ZipFile' class
# 'infolist()' returns a list containing all the folders and files of the zip -> 'ZipInfo' objects
# assigning last element of the list to a variable to test all the methods of 'ZipInfo'
archive = file.infolist()
read_me_file = archive[-1]
# 'ZipInfo' methods
# ZipInfo_object.filename returns the name of the file
print("Name of the file:- {}".format(read_me_file.filename))
# ZipInfo_object.file_size returns the size of the file
print("Size of the file:- {}".format(read_me_file.file_size))
# ZipInfo_object.is_dir() returns True if it's directory otherwise False
print("Is directory:- {}".format(read_me_file.is_dir()))
# ZipInfo_object.date_time() returns the created date & time of file
print("File created data & time:- {}".format(read_me_file.date_time))
if __name__ == '__main__': main()
Name of the file:- sample_file/READ ME.txt
Size of the file:- 59
Is directory:- False
File created data & time:- (2018, 10, 4, 11, 32, 22)
如果想进一步了解 ZipInfo 的对象,请访问 ZipInfo。
方法
方法是一段用于实现特定功能的代码块。例如,如果您想求一个数的绝对值,可以使用 Python 的内置方法 abs。
您可以在任何需要的地方使用这些方法。下面来看 zipfile 模块的一些方法。
zipfile.is_zipfile()
zipfile 模块的 is_zipfile(filename) 方法会在文件是有效 Zip 时返回 True,否则返回 False。
请看示例。
## zipfile.is_zip(filename)
import zipfile
def main():
print(zipfile.is_zipfile('sample_file.zip')) # it returns True
if __name__ == '__main__': main()
True
处理 Zip 文件
本节将学习如何处理 Zip 文件,如打开、解压、写入等。
解压 Zip 文件
使用 extractall 方法将 Zip 文件中的文件解压到当前目录。
## extracting zip file
import zipfile
def main():
# assigning filename to a variable
file_name = 'sample_file.zip'
# opening Zip using 'with' keyword in read mode
with zipfile.ZipFile(file_name, 'r') as file:
# printing all the information of archive file contents using 'printdir' method
print(file.printdir())
# extracting the files using 'extracall' method
print('Extracting all files...')
file.extractall()
print('Done!') # check your directory of zip file to see the extracted files
if __name__ == '__main__': main()
File Name Modified Size
sample_file/ 2018-10-04 11:33:22 0
sample_file/even_odd.py 2018-06-29 23:35:54 129
sample_file/READ ME.txt 2018-10-04 11:32:22 59
None
Extracting all files...
Done!
解压带密码的 Zip
要解压带密码的 Zip,需要为 extract(pwd = password) 或 extractall(pwd = password) 方法的 pwd 位置参数传入值。
密码必须以 bytes 形式传入。要将 str 转为 bytes,请使用 Python 的内置方法 bytes,并指定 utf-8 编码。
请看示例。
## extracting zip with password
import zipfile
def main():
file_name = 'pswd_file.zip'
pswd = 'datacamp'
with zipfile.ZipFile(file_name) as file:
# password you pass must be in the bytes you converted 'str' into 'bytes'
file.extractall(pwd = bytes(pswd, 'utf-8'))
if __name__ == '__main__': main()
您也可以通过 ZipFile 类的 setpassword(pwd = password) 方法来设置密码再解压。见下例。
## extracting zip with password
import zipfile
def main():
file_name = 'pswd_file.zip'
pswd = 'datacamp'
with zipfile.ZipFile(file_name) as file:
# 'setpassword' method is used to give a password to the 'Zip'
file.setpassword(pwd = bytes(pswd, 'utf-8'))
file.extractall()
if __name__ == '__main__': main()
创建 Zip 文件
创建 Zip 文件无需额外方法。只需将名称传给 ZipFile 类,它就会在当前目录创建一个归档。
参见示例。
## Creating Zip file
import zipfile
def main():
archive_name = 'example_file.zip'
# below one line of code will create a 'Zip' in the current working directory
with zipfile.ZipFile(archive_name, 'w') as file:
print("{} is created.".format(archive_name))
if __name__ == '__main__': main()
example_file.zip is created.
向 Zip 文件写入
要向 Zip 写入文件,需要以写入模式打开归档文件。它会覆盖 Zip 中所有现有文件。
来看一个示例。
## Writing files to zip
import zipfile
def main():
file_name = 'sample_file.zip'
# Opening the 'Zip' in writing mode
with zipfile.ZipFile(file_name, 'w') as file:
# write mode overrides all the existing files in the 'Zip.'
# you have to create the file which you have to write to the 'Zip.'
file.write('extra_file.txt')
print('File overrides the existing files')
# opening the 'Zip' in reading mode to check
with zipfile.ZipFile(file_name, 'r') as file:
print(file.namelist())
if __name__ == '__main__': main()
File overrides the existing files
['extra_file.txt']
向 Zip 追加文件
要向 Zip 追加文件,需要以追加(a)模式打开 Zip。它不会覆盖现有文件。
请看示例。
## Appending files to zip
import zipfile
def main():
file_name = 'sample_file.zip'
# opening the 'Zip' in writing mode
with zipfile.ZipFile(file_name, 'a') as file:
# append mode adds files to the 'Zip'
# you have to create the files which you have to add to the 'Zip'
file.write('READ ME.txt')
file.write('even_odd.py')
print('Files added to the Zip')
# opening the 'Zip' in reading mode to check
with zipfile.ZipFile(file_name, 'r') as file:
print(file.namelist())
if __name__ == '__main__': main()
Files added to the Zip
['extra_file.txt', 'READ ME.txt', 'even_odd.py']
到目前为止,您已经学习了如何处理 Zip 文件。现在您可以打开、写入、追加、解压、创建等。接下来,我们来编写一个小程序。
让我们看看要做什么?
使用循环和 zipfile 解压多个带密码的子 Zip
- 您有一个 Zip 文件,内部嵌套了多个子 Zip 文件。每个 Zip 的密码都是它们各自的文件名。我们的任务是不断解压这些 Zip,直到抵达最深层。
解决步骤
- 使用父级文件名作为密码解压父级文件。
- 使用 namelist() 方法获取第一个子级的名称,并将其存入变量。
-
运行一个无限循环。
-
使用 is_zipfile() 方法检查该文件是否为 Zip。如果是,执行以下操作:
-
使用 name 变量打开该 Zip。
-
从 name 变量中获取该 Zip 的密码。
-
解压该 Zip。
-
获取下一个 Zip 的名称并存入 name 变量。
-
-
否则
- 使用 break 跳出循环。
-
以上是我设计的步骤。您也可以根据自己的文件结构进行调整。
## Solution
import zipfile
def main():
# storing the parent name
parent_file_name = '000.zip'
with zipfile.ZipFile(parent_file_name, 'r') as parent_file:
# extracting the parent file
pswd = bytes(parent_file_name.split('.')[0], 'utf-8')
parent_file.extractall(pwd = pswd)
# getting the first child
next_zip_name = parent_file.namelist()[0]
# looping through the sub zips infinite times until you don't encouter a 'Zip' file
while True:
if zipfile.is_zipfile(next_zip_name):
# opening the zip
with zipfile.ZipFile(next_zip_name, 'r') as child_file:
# getting password from the zip name
pswd = bytes(next_zip_name.split('.')[0], 'utf-8')
# extracting the zip
child_file.extractall(pwd = pswd)
# getting the child zip name
next_zip_name = child_file.namelist()[0]
else:
break
if __name__ == '__main__': main()
运行上述程序后,您会看到所有子级 Zip 都被解压到当前目录。
结语
恭喜您完成本教程!
希望您喜欢这篇教程。本文在您处理 Zip 文件时会非常有用。现在,您已经能够处理 Zip 文件了。
如果您对文章有任何疑问,请在评论区留言。我会尽快回复。
再次提醒,如果您是 Python 初学者,可以学习 DataCamp 的免费课程 Intro to Python for Data Science,或阅读 Python 官方的文档。
祝您编码愉快!