5

可能重复:
如何在不阅读其全部内容的情况下跟踪压缩文件?

我有一个 7GB 的 gzip 系统日志文件,可以提取到超过 25GB。我只需要检索文件的第一行和最后一行,而不需要一次将整个文件读入内存。

GzipFile()在 Python 2.7 中允许使用with来读取头部(通过迭代with意味着我不必读取整个文件):

>>> from itertools import islice
>>> from gzip import GzipFile
>>> with GzipFile('firewall.4.gz') as file:
...     head = list(islice(file, 1))
>>> head
['Oct  2 07:35:14 192.0.2.1 %ASA-6-305011: Built dynamic TCP translation 
from INSIDE:192.0.2.40/51807 to OUTSIDE:10.18.61.38/2985\n']

Python 2.6 版本以避免诸如AttributeError: GzipFile instance has no attribute '__exit__'(因为GzipFile()不支持GzipFile(with )上的迭代)等问题...

>>> from itertools import islice
>>> from gzip import GzipFile
>>> class GzipFileHack(GzipFile):
...     def __enter__(self):
...         return self
...     def __exit__(self, type, value, tb):
...         self.close()
>>> with GzipFileHack('firewall.4.gz') as file:
...     head = list(islice(file, 1))

问题是我无法检索尾部...islice()不支持负值,并且如果不遍历 25GB 文件(这需要太长时间),我找不到检索最后一行的方法。

在不将整个文件读入内存或遍历所有行的情况下,读取 gzip 文本文件尾部的最有效方法是什么?如果无法做到这一点,请说明原因。

4

3 回答 3

13

gzip 使用的 deflate 格式部分地通过在紧接的前 32K 数据中的某处找到匹配的字符串并使用对具有偏移量和长度的字符串的引用来进行压缩。所以在任何时候,从那个点解压缩的能力取决于最后的 32K,它本身取决于它前面的 32K,依此类推回到开头。因此,要解压缩流中任意点x的数据,您需要先解压缩从0x-1 的所有内容。

有几种方法可以缓解这种情况。首先,如果您想频繁地随机访问一个 gzip 文件,那么您愿意完成一次扫描整个 gzip 文件并建立索引的工作。该索引将在其中保存在某些数量的入口点中的每一个处的先前 32K,其中这些入口点的密度决定了随机访问的速度。在zlib 源代码分发中,您可以在examples/zran.c中看到一个示例。

如果您可以控制 gzip 文件的生成,则可以使用Z_FULL_FLUSHflush 选项定期清除这些点上最后 32K 的历史记录,以允许随机访问条目。然后,您可以将这些点的位置保存为索引,这样就不需要每个入口点的 32K 历史块。如果这些点足够少,那么对压缩的影响就会微乎其微。

只需编写 gzip 输出的能力,您就可以Z_FULL_FLUSH通过简单地编写串联的 gzip 流来执行类似的操作,但需要更多的开销。 gunzip将接受并解码与命令放在一起的 gzip 流,cat并将写出单个未压缩数据流。您可以通过这种方式建立一个大的 gzip 日志,在某处记住每个 gzip 片段开始的偏移量。

如果您只对尾部感兴趣,那么您可以按照您在其中一个评论中的建议进行操作,即简单地在大型 gzip 文件尾部的其他位置维护缓存。

我不知道您是否正在制作日志文件。如果您是,您可能希望查看有效地将短日志消息附加到大型 gzip 文件的示例,该示例在 zlib 源代码分发中再次找到。

于 2012-10-06T17:10:50.517 回答
6

gzip 文件是一个流,因此您必须通读它才能到达最后一行

from gzip import GzipFile
from collections import deque
dq = deque(maxlen=1)
with GzipFile('firewall.4.gz') as file:
    head = next(file)
    dq.extend(file)
tail = dq[0]
于 2012-10-06T16:28:17.507 回答
5

没有办法这样做。DEFLATE 是一种流压缩算法,这意味着如果不解压缩之前的所有内容,就无法解压缩文件的任意部分。

于 2012-10-06T16:20:42.040 回答