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

时间:2018-02-13 11:19:13

标签: python gzip filesize

我有许多gzip压缩文本文件,我想在运行中解压缩并在线阅读和处理,这样我就可以节省磁盘空间,还可以节省从磁盘读取数据的时间,但代价是在线解压缩。

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

但是如何确定原始未压缩文件大小的大小,以便设置在完成之前读取的总字节数(未压缩)计数以跟踪进度?据我所知,在搜索网页时,gzip对于大于4千兆字节的文件很难解决这个问题。

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

我怎样才能实现?

以下是代码示例,其中的注释也反映了我想要实现的目标。

我使用的是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 个答案:

答案 0 :(得分:4)

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

EDIT2:BTW。当然,您也可以使用tell()实例上的GzipFile告诉您未解压缩的文件有多远(读取字节数)。

编辑:现在我看到这只是你问题的部分答案。你也需要总数。在那里,我担心你有点不走运。 ESP。对于超过4GB的文件,您已经注意到了。 gzip在最后四个字节中保持未压缩的大小,因此你可以跳转并读取它们并跳回(GzipFile似乎不会公开这些信息),但由于它是四个字节,你只能存储4GB作为最大数字,其余只是被截断到值的较低4B。在那种情况下,我恐怕你不会知道,直到最后。

无论如何,上面的提示会给你当前压缩和未压缩的位置,希望你能够至少在某种程度上达到你想要做的目的。

答案 1 :(得分:2)

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

答案 2 :(得分:0)

这是我所做的:

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)

希望这对您的文件有效。

答案 3 :(得分:0)

尝试自己实现此目的后,我找到了简单的解决方案(文档中并未明确说明)。当以文本形式打开时,您可以使用gzippedfile.buffer.fileobj访问基础文件对象,而对于二进制文件则可以使用gzippedfile.fileobj访问二进制文件,然后可以使用它们来获取光标的位置(读取的压缩字节数,如果通过该文件),方法是调用tell()

有关buffer的用法,请参见textIO wrapper doc,有关fileobj的{​​{3}}请参见

对于您而言,您可以执行以下操作:

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

如果您确实需要访问二进制文件,这是@Mark Adler建议的示例实现

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