前一章中我们学习了如何处理文件、目录和数据。我们还学习了tarfile模块。本章中,我们将学习文件的存档、加密和解密。存档在管理文件、目录和数据中扮演重要的角色。但首先什么是存档呢?存档是一个将多个文件和目录存放到一个文件中的过程。Python中有tarfile模块可用于创建这类存档文件。
本章中,我们将学习如下课题:
这一部分中,我们将学习如何使用Python中的shutil模块来创建和解压存档包。shutil模块带有make_archive()函数,可以创建一个新的存档文件。使用make_archive(),我们可以对包含的内容的整个目录进行打包。
下面我们来编写一个名为shutil_make_archive.py的脚本并加入如下内容:
import tarfile, shutil, sys
shutil.make_archive(
'work_sample', 'gztar',
root_dir='..',
base_dir='work'
)
print('Archive contents:')
with tarfile.open('work_sample.tar.gz', 'r') as t_file:
for names in t_file.getnames():
print(names)
运行程序,将得到如下输出:
$ python3 shutil_make_archive.py
Archive contents:
work
work/sample.txt
上例中,我们使用了Python中的shutil和tarfile模块来创建存档文件。在shutil.make_archive()中,我们指定了work_sample来作为存档文件的名称并使用 gz 格式。我们还通过基本目录属性指定了工作目录。最后,我们打印出了存档中的文件名称。
要解压存档包,可使用shutil中带有的unpack_archive()函数。使用该函数,我们可以提取存档的文件。我们传递存档包名以及想要提取内容的目录名。如果未传递目录名,就会将提取的内容放到当前工作目录中。
下面创建一个名为shutil_unpack_archive.py的脚本并编写如下代码:
import pathlib, shutil, sys, tempfile
with tempfile.TemporaryDirectory() as d:
shutil.unpack_archive('work_sample.tar.gz',
extract_dir='/home/student/work')
prefix_len = len(d) + 1
for extracted in pathlib.Path(d).rglob('*'):
print(str(extracted)[prefix_len:])
运行脚本如下:
$ python3 shutil_unpack_archive.py
下面检查work/目录,会在其中发现work/文件夹,并包含提取的文件。
这一部分我们将学习tarfile模块。我们还将学习输入文件名的测试,评估它是否是有效的存档文件名。我们会看如何将一个新文件加入到已有的存档文件中,如何使用tarfile模块读取元数据,以及如何使用extractall() 函数来从存档中提取文件。
首先,我们将测试输入的文件名是否是有效的存档文件。进行这一测试,tarfile模块带有is_tarfile()函数,返回的是布尔值。
创建一个名为check_archive_file.py的脚本并编写如下内容:
import tarfile
for f_name in ['hello.py', 'work.tar.gz', 'welcome.py', 'nofile.tar', 'sample.tar.xz']:
try:
print('{:} {}'.format(f_name, tarfile.is_tarfile(f_name)))
except IOError as err:
print('{:} {}'.format(f_name, err))
运行脚本,将得到如下输出:
$ python3 check_archive_file.py
hello.py False
work.tar.gz True
welcome.py False
nofile.tar [Errno 2] No such file or directory: 'nofile.tar'
sample.tar.xz False
因此,tarfile.is_tarfile()会检查列表出的各个文件名。hello.py, welcome.py文件不是tar包,所以得到的是布尔值False,work.tar.gz和sample.tar.xz是tar包,因此得到了布尔值True。并且目录中不存在nofile.tar这一文件,所以抛出了脚本中所编写的异常。
下面我们将向已创建的存档文件中添加一个新文件。创建名为add_to_archive.py的脚本并编写如下代码:
import shutil, os, tarfile
print('creating archive')
shutil.make_archive('work', 'tar', root_dir='..', base_dir='work',)
print('\nArchive contents:')
with tarfile.open('work.tar', 'r') as t_file:
for names in t_file.getnames():
print(names)
os.system('touch sample.txt')
print('adding sample.txt')
with tarfile.open('work.tar', mode='a') as t:
t.add('sample.txt')
print('contents:',)
with tarfile.open('work.tar', mode='r') as t:
print([m.name for m in t.getmembers()])
运行脚本,我们将得到如下输出:
$ python3 add_to_archive.py
# 输出结果:
creating archive
Archive contents:
work
work/work
work/work/sample.txt
adding sample.txt
contents:
['work', 'work/work', 'work/work/sample.txt', 'sample.txt']
本例中,首先我们使用shutil.make_archive()创建了一个压缩文件,然后我们打印出了存档文件中的内容。再后我们在下一条语句中创建了一个sample.txt文件。接着我们希望将sample.txt加到已创建的work.tar中。这里我们使用了追加模式。接下来我们再次读取了存档文件中的内容。
下面我们将学习如何读取存档文件的元数据。getmembers() 会加载文件的元数据。创建一个名为read_metadata.py的脚本并编写如下内容:
import tarfile, time
with tarfile.open('work.tar', 'r') as t:
for file_info in t.getmembers():
print(file_info.name)
print('Size :', file_info.size, 'bytes')
print('Type :', file_info.type)
print()
运行脚本,将会得到如下输出:
$ python3 read_metadata.py
work
Size : 0 bytes
Type : b'5'
work/work
Size : 0 bytes
Type : b'5'
work/work/sample.txt
Size : 0 bytes
Type : b'0'
sample.txt
Size : 0 bytes
Type : b'0'
下面我们将使用 extractall() 函数来从存档包中提取内容。为此,创建一个名为extract_contents.py的脚本并在其中编写如下代码:
import tarfile, os
os.mkdir('work')
with tarfile.open('work.tar', 'r') as t:
t.extractall('work')
print(os.listdir('work'))
运行脚本,将得到如下输出:
$ python3 extract_contents.py
# 输出结果:
['sample.txt', 'work']
查看当前工作目录,我们会看到work/ 目录,进入该目录就可以看到提取的文件。
这部分中,我们将使用ZIP文件。我们将学习Python中的zipfile模块,如何创建ZIP文件,如何测试输入的文件名是否是有效的ZIP文件名,读取元数据等等。
首先我们将学习如何使用shutil模块的make_archive()函数创建zip文件。创建一个名为make_zip_file.py的脚本并编写如下代码:
import shutil
shutil.make_archive('work', 'zip', 'work')
运行脚本如下:
$ python3 make_zip_file.py
此时查看当前工作目录,就会发现work.zip文件。
接下来我们将测试所输入的zip文件名是否有效。为此可使用zipfile模块中的is_zipfile()函数。
创建一个脚本check_zip_file.py并编写如下内容:
import zipfile
for f_name in ['hello.py', 'work.zip', 'welcome.py', 'sample.txt', 'test.zip']:
try:
print('{:} {}'.format(f_name, zipfile.is_zipfile(f_name)))
except IOError as err:
print('{:} {}'.format(f_name, err))
运行脚本如下:
$ python3 check_zip_file.py
# 输出结果:
hello.py False
work.zip True
welcome.py False
sample.txt False
test.zip False
在本例中,我们使用了for循环来检查列表中的文件名。is_zipfile()函数会逐一检查文件名并在结果中返回布尔值。
下面我们将来看如何使用Python中的zipfile模块来读取存档的ZIP文件的元数据。创建一个名为read_zip_metadata.py的脚本,并编写如下内容:
import zipfile
def meta_info(names):
with zipfile.ZipFile(names) as zf:
for info in zf.infolist():
print(info.filename)
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__":
meta_info('work.zip')
执行脚本如下:
$ python3 read_zip_metadata.py
make_zip_file.py
System : Unix
Zip Version : 20
Compressed : 49 bytes
Uncompressed: 57 bytes
sample.txt
System : Unix
Zip Version : 20
Compressed : 2 bytes
Uncompressed: 0 bytes
add_to_archive.py
System : Unix
Zip Version : 20
Compressed : 240 bytes
Uncompressed: 483 bytes
shutil_unpack_archive.py
System : Unix
Zip Version : 20
Compressed : 196 bytes
Uncompressed: 269 bytes
要获取zip文件的元数据信息,我们使用了ZipFile类的infolist()方法。
这一部分中我们将学习Python中的pyAesCrypt模块。pyAesCrypt是一个文件加密模块,使用AES256-CBC算法来对文件和二进制流文件加密和解密。
安装pyAesCrypt命令如下:
pip3 install pyAesCrypt
创建名为file_encrypt.py的脚本并编写如下代码:
import pyAesCrypt
from os import stat, remove
# encryption/decryption buffer size - 64K
bufferSize = 64 * 1024
password = '#Training'
with open('sample.txt', 'rb') as fIn:
with open('sample.txt.aes', 'wb') as fOut:
pyAesCrypt.encryptStream(fIn, fOut, password, bufferSize)
# get encrypted file size
encFileSize = stat('sample.txt.aes').st_size
运行脚本如下:
$ python3 file_encrypt.py
# 无输入内容
请查看当前工作目录,我们会发现其中有一个sample.txt.aes文件。
本例中,我们首先的提及了缓冲大小和密码。然后涉及了需要加密的文件名。在encryptStream中,我们传入了加密的文件fIn,以及加密后的文件名fOut。我们将加密后的文件存储为sample.txt.aes。
下面我们将对sample.txt.aes文件进行解密来获取文件的内容。创建一个名为file_decrypt.py 的脚本并编写如下内容:
import pyAesCrypt
from os import stat, remove
bufferSize = 64 * 1024
password = '#Training'
encFileSize = stat('sample.txt.aes').st_size
with open('sample.txt.aes', 'rb') as fIn:
with open('sampleout.txt', 'wb') as fOut:
try:
pyAesCrypt.decryptStream(fIn, fOut, password, bufferSize, encFileSize)
except ValueError:
remove('sampleout.txt')
运行脚本如下:
$ python3 file_decrypt.py
现在查看当前工作目录。一个名为sampleout.txt的文件会被创建。这就是我们的解密后文件。
本例中,我们涉及到了解密的文件名sample.txt.aes。然后,我们解密后的文件为sampleout.txt。在decryptStream中,涉及到了要解密的文件fIn,以及解密后文件的名称fOut。
本章中,我们学习了创建和提取存档文件。在管理文件、目录和数据时存档扮演着重要的角色。它还将许多文件和目录存入一个文件中。
我们深入学习了Python模块tarfile和zipfile,来让我们创建、提取和测试存档文件。我们可以将新文件添加到已有存档文件中、读取元数据、从存档中提取文件。我们还学习了使用pyAescrypt模块来进行文件加密和解密。
下一章中我们将学习Python 中的文本处理和正则表达式。Python有一称作正则表达式的非常强大的库,可以完成搜索和提取数据等任务。