代码之家  ›  专栏  ›  技术社区  ›  konstunn

在Python中读取gzip文本文件中的行并获取读取的原始压缩字节数

  •  3
  • konstunn  · 技术社区  · 7 年前

    我有许多gzip文本文件,我想解压缩并动态读取(在线)和处理,这样我可以节省磁盘空间,也可以节省从磁盘读取数据的时间,而牺牲在线解压缩的时间。

    所以我使用gzip模块和tqdm来跟踪进度。

    但是,我如何才能找出原始未压缩文件大小的大小,以便将总字节(未压缩)计数设置为在完成之前读取以跟踪进度?就我在搜索web时所关注的问题而言,对于大于4G字节的文件,在gzip中很难解决这个问题,这就是我的情况。

    或者,我应该跟踪读取的压缩字节数,将总字节数设置为压缩文件的大小。

    我怎样才能做到这一点?

    下面是代码示例,其中的注释也反映了我试图实现的目标。

    我正在使用Python 3.5。

    import gzip
    import tqdm
    import os
    
    size = os.path.getsize('filename.gz')
    pbar = tqdm.tqdm(total=size, unit='b', unit_scale=True, unit_divisor=1024)
    
    with gzip.open('filename.gz', 'rt') as file:
        for line in file:
            bytes_uncompressed = len(line.encode('utf-8'))
            # but how can I get compressed bytes read count?
            # bytes_compressed = ...?
    
            # pbar.update(bytes_compressed)
    
    4 回复  |  直到 7 年前
        1
  •  5
  •   Ondrej K.    7 年前

    您应该打开以读取基础文件(二进制模式) f = open('filename.gz', 'rb') . 然后在此基础上打开gzip文件。 g = gzip.GzipFile(fileobj=f) . 从执行读取操作 g ,告诉我你有多远,你这只猫 f.tell() 询问压缩文件中的位置。

    编辑2:顺便说一句。当然你也可以使用 tell() GzipFile 实例来告诉您查看未压缩文件的长度(读取的字节数)。

    编辑:现在我明白了,这只是你问题的部分答案。你还需要总数。恐怕你有点运气不好。特别是如您所述,对于超过4GB的文件。gzip在最后四个字节中保留未压缩的大小,因此您可以跳转到那里并读取它们并跳回( gzip文件 似乎不会公开此信息本身),但由于它是四个字节,因此只能将4GB存储为最大值,其余的则被截断为值中较低的4B。那样的话,恐怕你要到最后才会知道。

    总之,上面的提示给出了压缩和未压缩的当前位置,希望这能让您至少在一定程度上实现您的目标。

        2
  •  2
  •   Mark Adler    7 年前

    你的问题有答案。不要跟踪未压缩字节的进度。跟踪压缩字节的进度。对于自一致性压缩文件,它们之间大致成比例,因此您将获得相同的效果。很容易找到压缩文件的大小。

        3
  •  2
  •   Dash    4 年前

    在我自己尝试实现之后,我找到了一个简单的解决方案(文档中没有明确说明)。可以使用访问基础文件对象 gzippedfile.buffer.fileobj 以文本形式打开时,以及 gzippedfile.fileobj 以二进制文件打开时。

    如果在文件上迭代,则使用 tell() 将是从磁盘读取的字节数。

    请参见 textIO wrapper doc 对于 buffer 用法和 gzip doc 对于 fileobj

    在您的情况下,您可以执行以下操作:

    with open('filename.gz', 'rt') as file:
        for line in file:
            pbar.update(file.buffer.fileobj.tell() - pbar.n)   # tqdm uses incremental update, so 
                                                       # increment is (current - last value)
            # Do things
    

    如果您真的需要访问二进制文件,下面是@markadler建议的一个示例实现

    with open('filename.gz', 'rb') as f, gzip.open(f, 'rt') as file:
        for line in file:
            pbar.n = f.tell()  # Another way to set progress when we know total progress rather than increment
            pbar.update(0)   # Call refresh if needed
            # Do things
    
        4
  •  0
  •   Dylan    5 年前

    以下是我所做的:

    import gzip
    import tqdm
    import os
    
    
    def _reader_generator(reader):
        b = reader(1024 * 1024)
        while b:
            yield b
            b = reader(1024 * 1024)
    
    def raw_newline_count_gzip(fname):
        f = gzip.open(fname, 'rb')
        f_gen = _reader_generator(f.read)
        return sum(buf.count(b'\n') for buf in f_gen)
    
    num = raw_newline_count_gzip('filename.gz')
    
    with gzip.open('filename.gz', 'rt') as file:
        with tqdm(total=num) as pbar:
            for line in file:
                bytes_uncompressed = len(line.encode('utf-8'))
                # do whatever you want
    
                pbar.update(1)
    
    

    希望这对你的档案有用。