【问题标题】:Get uncompressed size of a .gz file in python在python中获取.gz文件的未压缩大小
【发布时间】:2009-11-09 22:43:45
【问题描述】:

使用 gzip,tell() 返回未压缩文件中的偏移量。
为了显示进度条,我想知道文件的原始(未压缩)大小。
有没有简单的方法可以找出来?

【问题讨论】:

    标签: python gzip


    【解决方案1】:

    未压缩的大小存储在 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
    • 这正是旧 Jorge Israel Peña 的答案中所显示的内容,因此虽然您的答案提供了一个方便的功能,但它并没有为主题增加太多内容。此外,正如 cmets 在旧答案中所说,仅取决于最后 4 个字节实际上并非 100% 万无一失,因为 GZ 允许您在文件末尾附加新块
    【解决方案2】:

    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 存储实际的未压缩大小。但是,我认为它没有公开,因此您可能必须破解它才能公开它。不太清楚,抱歉。

    我现在只是查看了所有这些,我还没有尝试过,所以我可能是错的。我希望这对你有一些用处。抱歉,如果我误解了您的问题。

    【讨论】:

    • 我想这已经足够了。如果文件大于 4G,很容易在进度条中添加一些启发式方法,将文件大小设置为 4G + ISIZE,如果 tell() 表明我们离 ISIZE 太近了。
    • 我需要做同样的事情,我正在尝试扩展 GzipFile 类以提供文件大小,但我不成功,你是如何让它工作的?
    • 更新:这个功能对我有用:code.activestate.com/lists/python-list/245777
    • 请注意,这并不是完全万无一失的,因为附加到的 gzip 文件只有最后附加部分的大小...请参阅:pastebin.com/82zyV3k9 - 这里的第二个“1000”应该实际上是 2000,但它只是附加的最后一个块的大小......
    【解决方案3】:

    尽管其他答案说了什么,最后四个字节并不是获取 gzip 文件未压缩长度的可靠方法。首先,gzip 文件中可能有多个成员,所以这只是最后一个成员的长度。其次,长度可能超过 4 GB,在这种情况下,最后四个字节表示长度模 232。不是长度。

    但是,对于您想要的,无需获取未压缩的长度。相反,您可以将进度条基于消耗的 input 数量,而不是 gzip 文件的长度,后者很容易获得。对于典型的同质数据,该进度条将显示与基于未压缩数据的进度条完全相同的内容。

    【讨论】:

    • 马克,我最近正在对 gzip 文件进行一些编程操作,并且经常在堆栈底部看到你的许多答案,只有一两票。我想人们不认识你。感谢您做出的巨大贡献,感谢您对压缩问题的回答,尽管没有得到认可。
    【解决方案4】:

    Unix方式:通过subprocess.call/os.popen使用“gunzip -l file.gz”,捕获并解析其输出。

    【讨论】:

    • 永远不要接触比我更老的操作系统...说真的:我正在寻找一个 python 解决方案,因为代码适用于所有平台。
    • Windows 至少有 24 或 25 年的历史。版本 1 大约在 1985 年左右问世。你几岁了?
    • 44.5(最后一次使用 Unix 是在 18 岁)
    • 请注意,这基本上与@Jorge Israel Peñaanswer 所建议的相同,因此对于大于4GB 的文件无效。
    【解决方案5】:

    .gz 的最后 4 个字节保存文件的原始大小

    【讨论】:

    【解决方案6】:

    我不确定性能,但这可以在不知道 gzip 魔术的情况下使用:

    with gzip.open(filepath, 'rb') as file_obj:
        file_size = file_obj.seek(0, io.SEEK_END)
    

    这也适用于其他(压缩的)流读取器,例如 bz2 或普通的 open

    编辑: 正如 cmets 中所建议的那样,第二行中的 2io.SEEK_END 取代,这绝对更具可读性,并且可能更具前瞻性。

    编辑: 仅适用于 Python 3。

    【讨论】:

    • file_size = file_obj.seek(0, io.SEEK_END)
    • Python 3!没有 Python 2 ! ValueError: Seek from end not supported。但是:struct.unpack 适用于 2.7!
    【解决方案7】:
        f = gzip.open(filename)
        # kludge - report uncompressed file position so progess bars
        # don't go to 400%
        f.tell = f.fileobj.tell
    

    【讨论】:

      【解决方案8】:

      查看gzip 模块的源代码,我发现GzipFile 的底层文件对象似乎是fileobj。所以:

      mygzipfile = gzip.GzipFile()
      ...
      mygzipfile.fileobj.tell()
      

      在此之前进行一些健全性检查可能会很好,例如使用hasattr 检查属性是否存在。

      不完全是公共 API,但是...

      【讨论】:

      • .tell() 效果很好。我正在寻找的是原始文件大小。
      【解决方案9】:

      GzipFile.size 存储未压缩的大小,但它只会在您读取文件时增加,因此您应该更喜欢 len(fd.read()) 而不是非公开的 GzipFile.size。

      【讨论】:

      • 如果文件很大怎么办?
      • 请注意,正如@allyourcode 在这里建议的那样,len(df.read()) 强制 Python 将整个文件保存在内存中。对于非常大的文件,这可能会使您的进程崩溃。
      【解决方案10】:

      这是@noroksolution的Python2版本

      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

      【讨论】:

        【解决方案11】:
        import gzip
        
        File = gzip.open("input.gz", "r")
        Size = gzip.read32(File)
        

        【讨论】:

          猜你喜欢
          • 2019-06-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-03-31
          • 2019-08-13
          相关资源
          最近更新 更多