如何列出gz文件的内容而不在python中提取它?

时间:2015-11-08 08:33:05

标签: python compression gzip extraction

我有一个.gz文件,我需要使用python获取其中的文件名。

此问题与github

相同

唯一的区别是我的文件是.gz而不是.tar.gz所以tarfile库在这里没有帮助我

我正在使用requests库来请求网址。响应是压缩文件。

以下是我用来下载文件的代码

response = requests.get(line.rstrip(), stream=True)
        if response.status_code == 200:
            with open(str(base_output_dir)+"/"+str(current_dir)+"/"+str(count)+".gz", 'wb') as out_file:
                shutil.copyfileobj(response.raw, out_file)
            del response

此代码下载名称为1.gz的文件。现在,如果我使用存档管理器打开文件,该文件将包含my_latest_data.json

之类的内容

我需要提取文件,输出为my_latest_data.json

以下是我用来提取文件的代码

inF = gzip.open(f, 'rb')
outfilename = f.split(".")[0]
outF = open(outfilename, 'wb')
outF.write(inF.read())
inF.close()
outF.close()

outputfilename变量是我在脚本中提供的字符串,但我需要真实的文件名(my_latest_data.json

4 个答案:

答案 0 :(得分:4)

你不能,因为Gzip不是档案格式。

这本身就是一个垃圾解释,所以让我打破这个比我在评论中做的更多......

只是压缩

“只是一个压缩系统”意味着Gzip对输入字节(通常来自文件)进行操作并输出压缩字节。你不知道里面的字节是代表多个文件还是只代表一个文件 - 它只是 一个已压缩的字节流。这就是为什么你可以通过网络接受gzip压缩数据的原因。其bytes_in - > bytes_out。

什么是清单?

清单是存档中的标题,用作存档的目录。请注意,现在我使用术语“归档”而不是“压缩的字节流”。归档意味着它是清单引用的文件或段的集合 - 压缩的字节流只是一个字节流。

Gzip中有什么内容?

.gz文件内容的简化描述是:

  1. 带有特殊编号的标头,表示其gzip,版本和时间戳(10字节)
  2. 可选标头;通常包括原始文件名(如果压缩目标是文件)
  3. 正文 - 一些压缩的有效载荷
  4. 结尾的CRC-32校验和(8字节)
  5. 就是这样。没有明显。

    另一方面,存档格式将在内部显示清单。这就是tar库的用武之地.Tar只是一种将一堆位拼凑成一个文件的方法,并在前面放置一个清单,让你知道原始文件的名称以及它们之前的大小。连接到存档。因此,.tar.gz如此普遍。

    有些实用程序允许您一次解压缩gzip压缩文件的某些部分,或者仅在内存中解压缩它,然后让您检查清单或其中可能存在的内容。但任何清单的详细信息都特定于其中包含的存档格式。

    请注意,这与 zip 存档不同。 Zip 一种存档格式,因此包含一个清单。 Gzip是一个压缩库,比如bzip2和朋友。

答案 1 :(得分:3)

使用Mark Adler回复中的提示和对gzip模块的一些检查我已经设置了这个从gzip文件中提取内部文件名的功能。我注意到GzipFile对象有一个名为_read_gzip_header()的私有方法,几乎​​得到了文件名,所以我根据它做了

import gzip

def get_gzip_filename(filepath):
    f = gzip.open(filepath)
    f._read_gzip_header()
    f.fileobj.seek(0)
    f.fileobj.read(3)
    flag = ord(f.fileobj.read(1))
    mtime = gzip.read32(f.fileobj)
    f.fileobj.read(2)
    if flag & gzip.FEXTRA:
        # Read & discard the extra field, if present
        xlen = ord(f.fileobj.read(1))
        xlen = xlen + 256*ord(f.fileobj.read(1))
        f.fileobj.read(xlen)
    filename = ''
    if flag & gzip.FNAME:
        while True:
            s = f.fileobj.read(1)
            if not s or s=='\000':
                break
            else:
                filename += s
    return filename or None

答案 2 :(得分:1)

正如另一个答案所述,你的问题只有在我取出复数时才有意义:“我有一个.gz文件,我需要在里面找到文件的名字它使用python。“

gzip标头中可能包含也可能没有文件名。 gzip实用程序通常会忽略标头中的名称,并解压缩到与.gz文件同名的文件,但剥离.gz。例如。您的1.gz会解压缩到名为1的文件,即使标头中包含文件名my_latest_data.json也是如此。 gzip的-N选项将使用标题中的文件名(以及标题中的时间戳),如果有的话。因此gzip -dN 1.gz会创建文件my_latest_data.json,而不是1

您可以通过手动处理标题在Python的标题中找到文件名。您可以在gzip specification

中找到详细信息
  1. 验证前三个字节是否为1f 8b 08
  2. 保存第四个字节。称之为flags。如果flags & 8为零,则放弃 - 标题中没有文件名。
  3. 跳过接下来的六个字节。
  4. 如果flags & 2不为零,则跳过两个字节。
  5. 如果flags & 4不为零,则读取接下来的两个字节。考虑到它们是以小端序排列,从这两个字节中取出一个整数,称之为xlen。然后跳过xlen个字节。
  6. 我们已经知道flags & 8不为零,因此您现在处于文件名。读取字节直到达到零字节。这些字节最多但不包括零字节是文件名。

答案 3 :(得分:1)

The Python 3 gzip library discards this information 但你可以采用链接周围的代码来做其他事情。

如本页其他答案所述,此信息无论如何都是可选的。但是,如果您需要查看它是否在那里,也不是不可能检索。

import struct


def gzinfo(filename):
    # Copy+paste from gzip.py line 16
    FTEXT, FHCRC, FEXTRA, FNAME, FCOMMENT = 1, 2, 4, 8, 16
    
    with open(filename, 'rb') as fp:
        # Basically copy+paste from GzipFile module line 429f
        magic = fp.read(2)
        if magic == b'':
            return False

        if magic != b'\037\213':
            raise ValueError('Not a gzipped file (%r)' % magic)

        method, flag, _last_mtime = struct.unpack("<BBIxx", fp.read(8))

        if method != 8:
            raise ValueError('Unknown compression method')

        if flag & FEXTRA:
            # Read & discard the extra field, if present
            extra_len, = struct.unpack("<H", fp.read(2))
            fp.read(extra_len)
        if flag & FNAME:
            fname = []
            while True:
                s = fp.read(1)
                if not s or s==b'\000':
                    break
                fname.append(s.decode('latin-1'))
            return ''.join(fname)
        
def main():
    from sys import argv
    for filename in argv[1:]:
        print(filename, gzinfo(filename))

if __name__ == '__main__':
    main()

这用模糊的 ValueError 异常替换了原始代码中的异常(如果您打算更广泛地使用它,您可能需要修复它,并将其转换为合适的模块,您可以import ) 并使用通用的 read() 函数而不是特定的 _read_exact() 方法,该方法经历了一些麻烦,以确保它得到它所请求的确切字节数(如果您愿意,也可以取消) .