【问题标题】:How to get uncompressed size of a > 4GB .gz file in python如何在python中获取> 4GB .gz文件的未压缩大小
【发布时间】:2019-06-17 03:46:25
【问题描述】:

所以this super interesting thread 已经在获取 .gz 文件的原始大小。事实证明,可以从 4 个文件结束字节中获得的大小“只是”在那里,以确保提取成功。但是:如果提取的数据大小低于 2**32 字节,则可以依赖它。 IE。 4 GB。

现在如果有超过 4 GB 的未压缩数据,.gz 中必须有多个成员!最后4个字节只表示最后一个chunk的未压缩大小!

那么我们如何获得其他块的结束字节呢? Reading the gzip specs我没看到长度

+=======================+
|...compressed blocks...|
+=======================+

好的。必须依赖于 CM - 压缩方法。这可能是deflate。让我们看看the RFC about iton page 11 它说“非压缩块”有一个 LEN 属性,但是当他们讲述压缩块时它变得很时髦......

我可以想象类似的东西

full_size = os.path.getsize(gz_path)
gz = gzip.open(gz_path)
pos = 0
size = 0
while True:
    try:
        head_len = get_header_length(gz, pos)
        block_len = get_block_length(gz, pos + head_len)
        size += get_orig_size(gz, pos + head_len + block_len)
        pos += head_len + block_len + 8
    except:
        break
print('uncompressed size of "%s" is: %i bytes' % (gz_path, full_size)

但是如何get_block_length?!? :|

这可能不是故意的,因为......“流数据”。但我现在不想放弃。 已经有一个大问题了:即使是 7zip 也能显示出如此大的 .gz 文件,而未压缩的大小正好是最后 4 个字节。

有人有其他想法吗?

【问题讨论】:

    标签: python byte gzip


    【解决方案1】:

    首先,不,不需要多个成员。 gzip 成员的长度没有限制。如果未压缩的数据超过 4 GB,则最后四个字节仅表示该长度模 232。具有超过 4 GB 未压缩数据的 gzip 文件实际上很可能是单个成员。

    其次,您可以拥有多个成员这一事实即使对于小型 gzip 文件也是如此。未压缩的数据不需要超过 4 GB,文件的最后四个字节就没有用了。

    可靠地确定 gzip 文件中未压缩数据量的唯一方法是对其进行解压缩。您不必将数据写出,但您必须处理整个 gzip 文件并计算未压缩的字节数。

    【讨论】:

    • 啊,模数!我懂了!由于 4 个字节仅用于内部检查 + crc 就足够了,对吗?我重读了模数,并认为 4 个字节的 int 只能表示最大 4GB ......这很可悲。
    【解决方案2】:

    我来这里是为了估计您正在寻找什么。最好的答案是 Mark Adler 给出的答案:确定 gzip 文件未压缩大小的唯一可靠方法是实际解压缩它。

    但我正在使用通常会产生良好结果的估计,但它可能会在边界处失败。假设是:

    • 文件中只有一个流
    • 与整个文件相比,流的压缩率相似

    想法是获取文件开头的压缩比(获取1M样本,解压并测量),用它从压缩大小中推断出未压缩的大小,最后将32个最低有效位替换为从 gzip 流中获得的大小模块 32。 需要注意的是 4GiB 边界的倍数,因为它可能会高估/低估大小并给出估计 +/-4GiB 的位移。

    代码是:

    from io import SEEK_END
    import os
    import pack
    import zlib
    
    def estimate_uncompressed_gz_size(filename):
        # From the input file, get some data:
        # - the 32 LSB from the gzip stream
        # - 1MB sample of compressed data
        # - compressed file size
        with open(filename, "rb") as gz_in:
            sample = gz_in.read(1000000)
            gz_in.seek(-4, SEEK_END)
            lsb = struct.unpack('I', gz_in.read(4))[0]
            file_size = os.fstat(gz_in.fileno()).st_size
    
        # Estimate the total size by decompressing the sample to get the
        # compression ratio so we can extrapolate the uncompressed size
        # using the compression ratio and the real file size
        dobj = zlib.decompressobj(31)
        d_sample = dobj.decompress(sample)
    
        compressed_len = len(sample) - len(dobj.unconsumed_tail)
        decompressed_len = len(d_sample)
    
        estimate = file_size * decompressed_len / compressed_len
    
        # 32 LSB to zero
        mask = ~0xFFFFFFFF
    
        # Kill the 32 LSB to be substituted by the data read from the file
        adjusted_estimate = (estimate & mask) | lsb
    
        return adjusted_estimate
    

    解决上述警告的方法可能是检查估计值和调整后的估计值之间的差异,如果大于 2GiB,则相应地添加/减去 4GiB。但最终,它总是一个估计,而不是一个可靠的数字。

    【讨论】:

    • 非常酷!非常感谢!我想知道:人们可能还可以在解压缩、优化 estimate 的同时进行测量。
    • 当然,你可以保留两个计数器:压缩大小和未压缩大小,随着它们接近整个文件,估计会越来越好。这段代码的重点是在不必解压缩所有内容的情况下估计大小,只需一个小样本。但是对于无论如何都必须解压缩并且出于任何原因对最终大小感兴趣的应用程序,这可能会起作用。虽然对于估计解压缩时间,跟踪压缩数据就足够了。
    猜你喜欢
    • 1970-01-01
    • 2021-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-10
    • 2011-12-30
    相关资源
    最近更新 更多