6.15.Compression_Archiving
import gzip
import os
import shutil
from toolkit.blog.util import get_work_dir
FileDir = os.path.join(get_work_dir(), "datasets/file")
gzip
gzip 模块中包含一些常量,这是 DEFLATE 算法生成的压缩文件的一些标记字段。
gzip.FCOMMENT
gzip.FEXTRA
gzip.FHCRC
gzip.FNAME
gzip.FTEXT
gzip.GzipFile
gzip.READ
gzip.WRITE
处理比特流
压缩内存中的比特流 (字节流):
gzip.compress(b"this is a test.")
b'\x1f\x8b\x08\x00\xcc\x88\xaa_\x02\xff+\xc9\xc8,V\x00\xa2D\x85\x92\xd4\xe2\x12=\x00\xc3\xf7\x06N\x0f\x00\x00\x00'
解压缩内存中的比特流:
该比特流必须是使用 gzip 对应的 DEFLATE 压缩算法得到的比特流,否则无法使用对应的算法解压缩。
gzip.decompress(gzip.compress(b"this is a test."))
b'this is a test.'
解压 .gz
文件
gzip-1.3.14.tar.gz 会被解压为 gzip-1.3.14.tar 文件。
gz_file_path = os.path.join(FileDir, "gzip-1.3.14.tar.gz")
ext_file_path = os.path.join(FileDir, "gzip-1.3.14.tar")
# 创建 gzip 对象
gz_file = gzip.GzipFile(gz_file_path)
with open(ext_file_path, "wb+") as f:
f.write(gz_file.read())
gz_file.close()
也可以使用 gzip.open
函数打开 gz 文件,实际这也是调用 gzip.GzipFile
类。
# gzip.open 相当于 gzip.GzipFile
with gzip.open(gz_file_path, "rb") as output:
with open(ext_file_path, "wb") as f:
f.write(output.read())
tarfile
import tarfile
import time
判断文件是否为tar文档,如果文件不存在,会引发IO ERROR。
tar_file_path = os.path.join(FileDir, "gzip-1.3.14.tar")
tarfile.is_tarfile(tar_file_path)
True
获取元数据
使用 .getnames()
来读取压缩文件中所有文件的文件名。
with tarfile.open(tar_file_path, "r") as t:
print(t.getnames()[0:5])
['gzip-1.3.14', 'gzip-1.3.14/zgrep.1', 'gzip-1.3.14/configure.ac',
'gzip-1.3.14/gzexe.1', 'gzip-1.3.14/gunzip.1']
通过 .getmembers()
和 .getmember()
函数来获取元数据。
with tarfile.open(tar_file_path, "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()
break
gzip-1.3.14
Modified: Sat Oct 31 02:54:33 2009
Mode : 0o777
Type : b'5'
Size : 0 bytes
如果一个所含文件的文件名已知,可使用 getmember()
函数获取其所对应的 TarInfo
对象。
with tarfile.open(tar_file_path, "r") as t:
for filename in ["gzip-1.3.14/doc/Makefile.in",
"gzip-1.3.14/doc/version.texi"]:
try:
info = t.getmember(filename)
except KeyError:
print("ERROR: Did not find {filename} in tar archive")
else:
print(f"{info.name} is {info.size:d} bytes")
gzip-1.3.14/doc/Makefile.in is 43510 bytes
gzip-1.3.14/doc/version.texi is 105 bytes
解压 tar 文件
可使用 extractfile()
方法读入压缩文档中某个成员的数据:
with tarfile.open(tar_file_path, "r") as t:
for filename in ["gzip-1.3.14/doc/Makefile.in"]:
try:
# 解压其中某个文件为二进制数据
f = t.extractfile(filename)
except KeyError:
print(f"ERROR: Did not find {filename} in tar archive")
else:
print(filename, ":")
print(f.read().decode("utf-8")[0:100])
gzip-1.3.14/doc/Makefile.in :
# Makefile.in generated by automake 1.11a from Makefile.am.
# @configure_input@
# Copyright (C) 199
解压 tar 压缩文档中文件到硬盘
out_dir = os.path.join(FileDir, "tar")
with tarfile.open(tar_file_path, "r") as t:
t.extract("gzip-1.3.14/doc/Makefile.in", out_dir)
print(os.listdir(out_dir))
['gzip-1.3.14']
解压整个 tar 压缩文档到硬盘:gzip-1.3.14.tar/gzip-1.3.14/..
→ tar/gzip-1.3.14/..
标准库文档中有一个注释提到
extractall()
方法的安全性强于extract()
,尤其适用于无法倒带读取输入的较早部分的流数据,所以大多数情况下应该使用extractall()
。
out_dir = os.path.join(FileDir, "tar")
with tarfile.open(tar_file_path, "r") as t:
t.extractall(out_dir)
print(os.listdir(out_dir))
['gzip-1.3.14']
extractall()
方法也可以只解压部分文件,这需要将待提取的文件名或者 TarInfo
元数据容器作为参数传递给 extractall()
。
with tarfile.open(tar_file_path, "r") as t:
t.extractall(out_dir, members=[t.getmember("gzip-1.3.14/doc/Makefile.in")], )
print(os.listdir(out_dir))
['gzip-1.3.14']
处理其他压缩文件
除了正常的 tar 归档文件,tarfile 模块还可处理通过 gzip 或 bzip2 协议压缩的归档文件。要打开一个压缩的归档文件,根据不同的压缩协议,传入 ":gz"
或 ":bz2"
模式参数到open()
函数。
# gzip-1.3.14.tar.gz/gzip-1.3.14.tar/gzip-1.3.14/.. → gz/gzip-1.3.14/..
gz_file_path = os.path.join(FileDir, "gzip-1.3.14.tar.gz")
out_dir = os.path.join(FileDir, "gz")
# 直接解压 tar.gz 文件
with tarfile.open(gz_file_path, mode="r:gz") as out:
out.extractall(out_dir)
print(os.listdir(out_dir))
['gzip-1.3.14']
# gzip.bzip2/gzip/.. → gzip/..
bz2_file_path = os.path.join(FileDir, "gzip.bz2")
out_dir = os.path.join(FileDir, "gzip")
# 直接解压 .bz2 文件
with tarfile.open(bz2_file_path, mode="r:bz2") as out:
out.extractall(out_dir)
print(os.listdir(out_dir))
['.tarball-version', 'aclocal.m4', 'algorithm.doc', 'amiga', 'atari',
'AUTHORS', 'bits.c', 'build-aux', 'ChangeLog', 'ChangeLog-2007',
'configure', 'configure.ac', 'COPYING', 'crypt.c', 'crypt.h',
'deflate.c', 'doc', 'GNUmakefile', 'gunzip.1', 'gunzip.in', 'gzexe.1',
'gzexe.in', 'gzip.1', 'gzip.c', 'gzip.doc', 'gzip.h', 'inflate.c',
'INSTALL', 'lib', 'lzw.c', 'lzw.h', 'm4', 'maint.mk', 'Makefile.am',
'Makefile.in', 'msdos', 'NEWS', 'nt', 'os2', 'primos', 'README',
'revision.h', 'sample', 'tailor.h', 'tests', 'THANKS', 'TODO',
'trees.c', 'unlzh.c', 'unlzw.c', 'unpack.c', 'unzip.c', 'util.c',
'vms', 'zcat.1', 'zcat.in', 'zcmp.1', 'zcmp.in', 'zdiff.1',
'zdiff.in', 'zegrep.in', 'zfgrep.in', 'zforce.1', 'zforce.in',
'zgrep.1', 'zgrep.in', 'zip.c', 'zless.1', 'zless.in', 'zmore.1',
'zmore.in', 'znew.1', 'znew.in']
bz2
解压 bz2 文件
import bz2
bz2_file_path = os.path.join(FileDir, "gzip.bz2")
out_dir = os.path.join(FileDir, "gzip")
# gzip.bzip2/gzip/.. → gzip
with bz2.open(bz2_file_path, mode="rb") as out:
with open(out_dir, "wb") as f:
f.write(out.read())
zipfile
解压 zip 文件
import zipfile
zip_file_path = os.path.join(FileDir, "gzip.zip")
out_dir = os.path.join(FileDir, "zip")
# gzip.zip/.. → zip/..
with zipfile.ZipFile(zip_file_path) as zf:
zf.extractall(out_dir)