使用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]
gzip格式指定了一个叫做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,在这种情况下,最后四个字节表示长度 modulo 232,而不是长度本身。
然而,对于您想要的内容,没有必要获得未压缩长度。相反,您可以基于已经获得的gzip文件的长度来计算进度条,此时进度条是基于已消耗的输入量与gzip文件长度之比而显示的。对于典型的同构数据,这种进度条将显示与基于未压缩数据的进度条完全相同的信息。
rows pgimport
),我使用已读取的未压缩数据量实现了一个进度条,同时还添加了总未压缩大小的估计值。如果程序发现估计值错误,它会将其更新为已读取的总量(此答案提供了超过4GiB的Python代码来估算大小)。 - Álvaro Justenos.stat()
函数来获取压缩后的文件大小。我更喜欢在进度条上看到未压缩的文件大小,因为这是将要插入数据库的数量。 - Álvaro Justen我不确定性能如何,但是可以通过以下方法实现,而无需了解 gzip
的魔术:
with gzip.open(filepath, 'rb') as file_obj:
file_size = file_obj.seek(0, io.SEEK_END)
这也适用于其他(压缩的)流读取器,如bz2
或普通的open
。
编辑:
如评论中建议的那样,第二行中的2
被替换为io.SEEK_END
,这显然更易读,可能更具未来性。
编辑: 仅适用于Python 3。
file_obj.seek(0, io.SEEK_END)
(或者顺便提一下,os.SEEK_END
)需要很长时间 - 是否需要解压缩才能到达流的末尾? - Maksym Ganenko 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将整个文件保存在内存中。对于非常大的文件,这可能会导致进程崩溃。 - Chen Levy
"rb"
模式来避免error: unpack requires a string argument of length 4
错误。 - slv