Python中的Tar文件:我能通过仅提取部分数据来更有效地解压缩吗?

9

我正在从USGS订购大量的Landsat场景,它们以tar.gz归档文件的形式提供。我正在编写一个简单的Python脚本来解压它们。每个归档文件包含15个大小在60-120 MB之间的tiff图像,总共超过2 GB。我可以使用以下代码轻松地提取整个归档文件:

import tarfile
fileName = "LT50250232011160-SC20140922132408.tar.gz"
tfile = tarfile.open(fileName, 'r:gz')
tfile.extractall("newfolder/")

实际上,我只需要这15个tiff文件中的6个,这些在标题中被标识为“bands”。它们是一些较大的文件,因此它们共占了约一半的数据。所以,我认为可以通过以下方式修改代码来加快这个过程:

fileName = "LT50250232011160-SC20140922132408.tar.gz"
tfile = tarfile.open(fileName, 'r:gz')
membersList = tfile.getmembers()
namesList = tfile.getnames()
bandsList = [x for x, y in zip(membersList, namesList) if "band" in y]
print("extracting...")
tfile.extractall("newfolder/",members=bandsList)

然而,将计时器添加到两个脚本中并没有显示出第二个脚本的显着效率提高(在我的系统上,单个场景中两者都运行约一分钟)。虽然提取速度有所加快,但似乎这种增益被找出需要首先提取哪些文件所需的时间所抵消了。
问题是,这种权衡是我所做的事情固有的,还是只是我的代码效率低下的结果?我对python相对较新,今天才发现tarfile,因此如果后者是真的,我不会感到惊讶,但我还没有能够找到任何关于有效提取归档部分的建议。
谢谢!
2个回答

11

您可以更高效地完成这项任务,通过将tar文件作为流打开。( https://docs.python.org/2/library/tarfile.html#tarfile.open )

mkdir tartest
cd tartest/
dd if=/dev/urandom of=file1 count=100 bs=1M
dd if=/dev/urandom of=file2 count=100 bs=1M
dd if=/dev/urandom of=file3 count=100 bs=1M
dd if=/dev/urandom of=file4 count=100 bs=1M
dd if=/dev/urandom of=file5 count=100 bs=1M
cd ..
tar czvf test.tgz tartest

现在阅读如下:
import tarfile
fileName = "test.tgz"
tfile = tarfile.open(fileName, 'r|gz')
for t in tfile:
    if "file3" in t.name: 
        f = tfile.extractfile(t)
        if f:
            print(len(f.read()))

请注意打开命令中的竖线|。我们只读取file3文件。
$ time python test.py

104857600

real    0m1.201s
user    0m0.820s
sys     0m0.377s

如果我将r|gz改回r:gz,则会得到:

$ time python test.py 
104857600

real    0m7.033s
user    0m6.293s
sys     0m0.730s

大致快了5倍(因为我们有5个大小相同的文件)。这是因为标准的打开方式允许向后查找;在压缩的tar文件中,只能通过提取来实现(我不知道确切的原因)。如果你以流的形式打开,就不能再随机查找了,但如果按顺序读取,在你的情况下是可能的,速度会更快。然而,在此之前你不能再使用getnames。但在这种情况下是不必要的。


1
我得到了相同的速度。也许tarfile已经针对非流情况进行了改进... - user3204459
非常感谢,这对我非常有帮助。使用'r|gz'比原来快了一个数量级。因此,我可以轻松地放弃在tar流中返回的能力。Python 2.7.18(tarfile修订版85213) - user8162

10
问题在于tar文件没有中央文件列表,而是在每个文件前顺序存储带有 头信息 的文件。然后通过gzip压缩tar文件以得到tar.gz。对于tar文件,如果您不想提取某个文件,则只需跳过存档中下一个header->size字节,然后读取下一个头信息。如果存档另外被压缩,您仍然需要跳过那么多字节,只不过不是在存档文件内而是在解压缩的数据流中(对于某些压缩格式可以工作,但对于其他格式则需要您在中间解压缩所有内容)

gzip属于后一类压缩方案。因此,虽然您可以节省不将不需要的文件写入磁盘的时间,但您的代码仍会对它们进行解压缩。您可能可以通过为非gzip档案覆盖_Stream class来克服这个问题,但对于您的gz文件,您无能为力。


1
你仍然可以节省提取文件的I/O成本。 - o11c
有趣。这让我觉得gzip格式有些受限,但我想这使压缩更有效率。谢谢你的信息! - Joe
不,它不会。结合存档和压缩的格式也能够在所有包含的文件之间共享压缩表,因此效率并不低。tar不这样做的原因是它遵循Unix哲学:归档和压缩是两个不同的问题,因此应该由单独的程序处理。你的问题展示了这种方法的一个缺点。 - Phillip

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接