【发布时间】:2009-11-09 22:43:45
【问题描述】:
使用 gzip,tell() 返回未压缩文件中的偏移量。
为了显示进度条,我想知道文件的原始(未压缩)大小。
有没有简单的方法可以找出来?
【问题讨论】:
使用 gzip,tell() 返回未压缩文件中的偏移量。
为了显示进度条,我想知道文件的原始(未压缩)大小。
有没有简单的方法可以找出来?
【问题讨论】:
未压缩的大小存储在 gzip 文件的最后 4 个字节中。我们可以读取二进制数据并将其转换为 int。 (这仅适用于 4GB 以下的文件)
import struct
def getuncompressedsize(filename):
with open(filename, 'rb') as f:
f.seek(-4, 2)
return struct.unpack('I', f.read(4))[0]
【讨论】:
"rb",避免error: unpack requires a string argument of length 4。
gzip format 指定了一个名为 ISIZE 的字段:
这包含原始(未压缩)输入数据的大小,模 2^32。
在gzip.py 中,我假设您正在使用它来支持gzip,有一个名为_read_eof 的方法定义如下:
def _read_eof(self):
# We've read to the end of the file, so we have to rewind in order
# to reread the 8 bytes containing the CRC and the file size.
# We check the that the computed CRC and size of the
# uncompressed data matches the stored values. Note that the size
# stored is the true file size mod 2**32.
self.fileobj.seek(-8, 1)
crc32 = read32(self.fileobj)
isize = U32(read32(self.fileobj)) # may exceed 2GB
if U32(crc32) != U32(self.crc):
raise IOError, "CRC check failed"
elif isize != LOWU32(self.size):
raise IOError, "Incorrect length of data produced"
您可以看到ISIZE 字段正在被读取,但只是为了将其与self.size 进行比较以进行错误检测。这应该意味着GzipFile.size 存储实际的未压缩大小。但是,我认为它没有公开,因此您可能必须破解它才能公开它。不太清楚,抱歉。
我现在只是查看了所有这些,我还没有尝试过,所以我可能是错的。我希望这对你有一些用处。抱歉,如果我误解了您的问题。
【讨论】:
尽管其他答案说了什么,最后四个字节并不是获取 gzip 文件未压缩长度的可靠方法。首先,gzip 文件中可能有多个成员,所以这只是最后一个成员的长度。其次,长度可能超过 4 GB,在这种情况下,最后四个字节表示长度模 232。不是长度。
但是,对于您想要的,无需获取未压缩的长度。相反,您可以将进度条基于消耗的 input 数量,而不是 gzip 文件的长度,后者很容易获得。对于典型的同质数据,该进度条将显示与基于未压缩数据的进度条完全相同的内容。
【讨论】:
Unix方式:通过subprocess.call/os.popen使用“gunzip -l file.gz”,捕获并解析其输出。
【讨论】:
.gz 的最后 4 个字节保存文件的原始大小
【讨论】:
我不确定性能,但这可以在不知道 gzip 魔术的情况下使用:
with gzip.open(filepath, 'rb') as file_obj:
file_size = file_obj.seek(0, io.SEEK_END)
这也适用于其他(压缩的)流读取器,例如 bz2 或普通的 open。
编辑:
正如 cmets 中所建议的那样,第二行中的 2 被 io.SEEK_END 取代,这绝对更具可读性,并且可能更具前瞻性。
编辑: 仅适用于 Python 3。
【讨论】:
file_size = file_obj.seek(0, io.SEEK_END)
ValueError: Seek from end not supported。但是:struct.unpack 适用于 2.7!
f = gzip.open(filename)
# kludge - report uncompressed file position so progess bars
# don't go to 400%
f.tell = f.fileobj.tell
【讨论】:
查看gzip 模块的源代码,我发现GzipFile 的底层文件对象似乎是fileobj。所以:
mygzipfile = gzip.GzipFile()
...
mygzipfile.fileobj.tell()
?
在此之前进行一些健全性检查可能会很好,例如使用hasattr 检查属性是否存在。
不完全是公共 API,但是...
【讨论】:
GzipFile.size 存储未压缩的大小,但它只会在您读取文件时增加,因此您应该更喜欢 len(fd.read()) 而不是非公开的 GzipFile.size。
【讨论】:
len(df.read()) 强制 Python 将整个文件保存在内存中。对于非常大的文件,这可能会使您的进程崩溃。
import gzip, io
with oepn("yourfile.gz", "rb") as f:
prev, cur = 0, f.seek(1000000, io.SEEK_CUR)
while prev < cur:
prev, cur = cur, f.seek(1000000, io.SEEK_CUR)
filesize = cur
请注意,就像f.seek(0, io.SEEK_END) 一样,这对于大文件来说很慢,但它会克服4GB size limitation 建议的更快解决方案here
【讨论】:
import gzip
File = gzip.open("input.gz", "r")
Size = gzip.read32(File)
【讨论】: