如何在Python中确定gzipped文件的Content-Length?

4 投票
3 回答
4726 浏览
提问于 2025-04-18 10:32

我有一个很大的压缩文件,我想知道里面内容的大小,但不想解压缩它。我试过这个:

import gzip
import os

with gzip.open(data_file) as f:
          f.seek(0, os.SEEK_END)
          size = f.tell()

但是我遇到了这个错误

ValueError: Seek from end not supported 

我该怎么做呢?

谢谢。

3 个回答

2

很遗憾,Python 2.x的gzip模块似乎没有办法直接获取未压缩文件的大小。

不过,gzip确实会在文件的最后部分存储未压缩文件的大小,这个大小是以小端格式的32位无符号整数存储的。你可以在这里找到更多信息:http://www.abeel.be/content/determine-uncompressed-size-gzip-file

不过,不幸的是,这种方法只适用于小于4GB的文件,因为gzip格式只使用了32位整数;具体可以查看手册

import os
import struct

with open(data_file,"rb") as f:
    f.seek(-4, os.SEEK_END)
    size, = struct.unpack("<I", f.read(4))
    print size
2

原则上来说,要准确知道一个gzip文件里未压缩数据的大小,是不可能的,除非你把它解压缩。你不需要有空间来存放未压缩的数据——你可以在解压的过程中把它丢掉。但你必须把所有的数据都解压出来。

如果你能控制这个gzip文件的来源,并且能确保以下几点:a) gzip文件里没有连接在一起的多个部分,b) 未压缩的数据长度小于4GB,c) gzip文件的末尾没有多余的垃圾数据,那么只有在这种情况下,你可以读取gzip文件最后四个字节,得到一个小端整数,这个整数就是未压缩数据的长度。

想了解更多细节,可以查看 这个回答

下面是Python代码,用来读取一个gzip文件并打印未压缩数据的长度,而不需要存储或保存未压缩的数据。它将内存使用限制在小缓冲区内。这需要Python 3.3或更高版本:

#!/usr/local/bin/python3.4
import sys
import zlib
import warnings
f = open(sys.argv[1], "rb")
total = 0
buf = f.read(1024)
while True:             # loop through concatenated gzip streams
    z = zlib.decompressobj(15+16)
    while True:         # loop through one gzip stream
        while True:     # go through all output from one input buffer
            total += len(z.decompress(buf, 4096))
            buf = z.unconsumed_tail
            if buf == b"":
                break
        if z.eof:
            break       # end of a gzip stream found
        buf = f.read(1024)
        if buf == b"":
            warnings.warn("incomplete gzip stream")
            break
    buf = z.unused_data
    z = None
    if buf == b"":
        buf == f.read(1024)
        if buf == b"":
            break
print(total)
-2

总结一下,我需要打开非常大的压缩文件(超过4GB),所以Dan的方法不适用。而且我想知道文件的长度(行数),所以Mark Adler的方法也不合适。

最后,我找到了一个适用于未压缩文件的解决方案(虽然不是最优化的,但可以用!),这个方法也可以很容易地应用到压缩文件上:

size = 0

with gzip.open(data_file) as f:
  for line in f:
    size+= 1
    pass

return size

谢谢大家,这个论坛的人真有效率!

撰写回答