9.5. tarfile — 访问 Tar 压缩文件
目的:Tar 存档访问。
tarfile
模块提供对 Unix tar 存档的读写访问,包括压缩文件。除了POSIX 标准之外,还支持几种 GNU tar 扩展。还提供处理 Unix 特殊文件类型,如硬链接和软链接,以及设备节点。
注意
虽然
tarfile
实现了 Unix 格式,但它也可用于在 Microsoft Windows 下创建和读取 tar 存档。
测试 Tar 文件
is_tarfile()
函数返回一个布尔值,指示作为参数传递的文件名是否指向有效的 tar 存档。
tarfile_is_tarfile.py
import tarfile
for filename in ['README.txt', 'example.tar',
'bad_example.tar', 'notthere.tar']:
try:
print('{:>15} {}'.format(filename, tarfile.is_tarfile(
filename)))
except IOError as err:
print('{:>15} {}'.format(filename, err))
如果文件不存在,is_tarfile()
会引发一个 IOError
。
$ python3 tarfile_is_tarfile.py
README.txt False
example.tar True
bad_example.tar False
notthere.tar [Errno 2] No such file or directory:
'notthere.tar'
从Tar压缩文档中读取元数据 (Metadata)
直接使用 TarFile
类来操作一个tar压缩文件。这个类不仅可用来读取数据,而且可用来添加文件到压缩文件中。
使用getnames()
来读取压缩文件中所有文件的文件名。
tarfile_getnames.py
import tarfile
with tarfile.open('example.tar', 'r') as t:
print(t.getnames())
该函数的返回一个字符串列表,包含了所有所含文件的文件名。
$ python3 tarfile_getnames.py
['index.rst', 'README.txt']
除了文件名,其他元数据信息可以通过使用TarInfo
类的实例来获取。
tarfile_getmembers.py
import tarfile
import time
with tarfile.open('example.tar', 'r') as t:
for member_info in t.getmembers():
print(member_info.name)
print(' Modified:', time.ctime(member_info.mtime))
print(' Mode :', oct(member_info.mode))
print(' Type :', member_info.type)
print(' Size :', member_info.size, 'bytes')
print()
通过 getmembers()
和 getmember()
函数来获取元数据。
$ python3 tarfile_getmembers.py
index.rst
Modified: Fri Aug 19 16:27:54 2016
Mode : 0o644
Type : b'0'
Size : 9878 bytes
README.txt
Modified: Fri Aug 19 16:27:54 2016
Mode : 0o644
Type : b'0'
Size : 75 bytes
如果一个所含文件的文件名已知,可使用getmember()
函数获取其所对应的 TarInfo
对象。
tarfile_getmember.py
import tarfile
import time
with tarfile.open('example.tar', 'r') as t:
for filename in ['README.txt', 'notthere.txt']:
try:
info = t.getmember(filename)
except KeyError:
print('ERROR: Did not find {} in tar archive'.format(
filename))
else:
print('{} is {:d} bytes'.format(
info.name, info.size))
如果文件名所对应的文件不存在压缩文件中,函数 getmember()
会抛出一个 KeyError
异常。
$ python3 tarfile_getmember.py
README.txt is 75 bytes
ERROR: Did not find notthere.txt in tar archive
从归档中提取文件
如果需要在程序中的某个归档成员中访问数据,可使用 extractfile()
方法,并把归档成员名作为参数传入。
tarfile_extractfile.py
import tarfile
with tarfile.open('example.tar', 'r') as t:
for filename in ['README.txt', 'notthere.txt']:
try:
f = t.extractfile(filename)
except KeyError:
print('ERROR: Did not find {} in tar archive'.format(
filename))
else:
print(filename, ':')
print(f.read().decode('utf-8'))
返回值是一个类似文件的对象,从该对象中可以读取归档成员的内容。
$ python3 tarfile_extractfile.py
README.txt :
The examples for the tarfile module use this file and
example.tar as data.
ERROR: Did not find notthere.txt in tar archive
若想对归档行解包操作,并将文件写入文件系统内,可以使用 extract()
或 extractall()
取代之前的方法。
tarfile_extract.py
import tarfile
import os
os.mkdir('outdir')
with tarfile.open('example.tar', 'r') as t:
t.extract('README.txt', 'outdir')
print(os.listdir('outdir'))
归档中的成员被从归档中读取并写入文件系统,这一过程开始于由参数中命名的那个路径之内。
$ python3 tarfile_extract.py
['README.txt']
标准库文档中有一个注释提到 extractall()
方法的安全性强于 extract()
,尤其是在处理不能回滚读取较早时间输入部分的流式数据的情况下,所以前者应当更广泛地应用。
tarfile_extractall.py
import tarfile
import os
os.mkdir('outdir')
with tarfile.open('example.tar', 'r') as t:
t.extractall('outdir')
print(os.listdir('outdir'))
extractall()
的第一个参数是文件被写入路径的名称。
$ python3 tarfile_extractall.py
['README.txt', 'index.rst']
若需从归档中提取特定的文件,可将需提取的文件名或者 TarInfo
元数据容器作为参数传递给 extractall()
。
tarfile_extractall_members.py
import tarfile
import os
os.mkdir('outdir')
with tarfile.open('example.tar', 'r') as t:
t.extractall('outdir',
members=[t.getmember('README.txt')],
)
print(os.listdir('outdir'))
在提供了 members
列表的情况下,只有列表中提名的文件会被提取。
$ python3 tarfile_extractall_members.py
['README.txt']
创建一个新的归档文件
使用带'w'
模式的TarFile
来创建一个新的归档文件。
tarfile_add.py
import tarfile
print('creating archive')
with tarfile.open('tarfile_add.tar', mode='w') as out:
print('adding README.txt')
out.add('README.txt')
print()
print('Contents:')
with tarfile.open('tarfile_add.tar', mode='r') as t:
for member_info in t.getmembers():
print(member_info.name)
已有的文件会被清除,同时创立一个新的归档文件。使用 add()
函数来添加新成员到新建的归档文件中。
$ python3 tarfile_add.py
creating archive
adding README.txt
Contents:
README.txt
以新名字来归档成员
当添加新文件到归档文件时,可以使用不同的文件名。这个可通过构造一个带有arcname
的TarInfo
对象,并将其传入addfile()
函数来实现。
tarfile_addfile.py
import tarfile
print('creating archive')
with tarfile.open('tarfile_addfile.tar', mode='w') as out:
print('adding README.txt as RENAMED.txt')
info = out.gettarinfo('README.txt', arcname='RENAMED.txt')
out.addfile(info)
print()
print('Contents:')
with tarfile.open('tarfile_addfile.tar', mode='r') as t:
for member_info in t.getmembers():
print(member_info.name)
该归档只含有一个重命名的文件:
$ python3 tarfile_addfile.py
creating archive
adding README.txt as RENAMED.txt
Contents:
RENAMED.txt
从文件以外的源来写入数据
有时候,我们需要直接从内存中将数据写进压缩包,而不是先将数据写入文件,再将文件添加进压缩包。你可以使用 addfile()
来从类似于打开文件的句柄添加数据来返回字节。
tarfile_addfile_string.py
import io
import tarfile
text = 'This is the data to write to the archive.'
data = text.encode('utf-8')
with tarfile.open('addfile_string.tar', mode='w') as out:
info = tarfile.TarInfo('made_up_file.txt')
info.size = len(data)
out.addfile(info, io.BytesIO(data))
print('Contents:')
with tarfile.open('addfile_string.tar', mode='r') as t:
for member_info in t.getmembers():
print(member_info.name)
f = t.extractfile(member_info)
print(f.read().decode('utf-8'))
通过首先构造 TarInfo
对象, 可以为压缩包成员指定你想要的任意名称。在设置了其大小之后,使用 addfile()
和 BytesIO
缓冲区作为数据源将数据写进压缩包中。
$ python3 tarfile_addfile_string.py
Contents:
made_up_file.txt
This is the data to write to the archive.
添加新成员
除了创建新的归档文件,还可以通过设置模式参数为'a'
来添加新文件到已有的归档文件。
tarfile_append.py
import tarfile
print('creating archive')
with tarfile.open('tarfile_append.tar', mode='w') as out:
out.add('README.txt')
print('contents:',)
with tarfile.open('tarfile_append.tar', mode='r') as t:
print([m.name for m in t.getmembers()])
print('adding index.rst')
with tarfile.open('tarfile_append.tar', mode='a') as out:
out.add('index.rst')
print('contents:',)
with tarfile.open('tarfile_append.tar', mode='r') as t:
print([m.name for m in t.getmembers()])
最终的归档文件包含了两个成员:
$ python3 tarfile_append.py
creating archive
contents:
['README.txt']
adding index.rst
contents:
['README.txt', 'index.rst']
处理压缩的归档文件
除了正常的Tar 归档文件,tarfile
模块还可处理通过gzip或bzip2协议压缩的归档文件。要打开一个压缩的归档文件,根据不同的压缩协议,传入 ":gz"
或 ":bz2"
模式参数到 open()
函数。
tarfile_compression.py
import tarfile
import os
fmt = '{:<30} {:<10}'
print(fmt.format('FILENAME', 'SIZE'))
print(fmt.format('README.txt', os.stat('README.txt').st_size))
FILES = [
('tarfile_compression.tar', 'w'),
('tarfile_compression.tar.gz', 'w:gz'),
('tarfile_compression.tar.bz2', 'w:bz2'),
]
for filename, write_mode in FILES:
with tarfile.open(filename, mode=write_mode) as out:
out.add('README.txt')
print(fmt.format(filename, os.stat(filename).st_size),
end=' ')
print([
m.name
for m in tarfile.open(filename, 'r:*').getmembers()
])
如果使用"r:*"
模式读取一个归档文件时,tarfile
会自动识别压缩方法。
$ python3 tarfile_compression.py
FILENAME SIZE
README.txt 75
tarfile_compression.tar 10240 ['README.txt']
tarfile_compression.tar.gz 213 ['README.txt']
tarfile_compression.tar.bz2 199 ['README.txt']
参考
- tarfile 的标准文档
- GNU tar 手册 -- Tar格式的文档及其扩展
zipfile
-- 类似的 ZIP 文档处理gzip
-- 读写GNU zip 压缩bz2
-- bzip2 压缩方法
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。