【问题标题】:How to read filenames included into a gz file如何读取包含在 gz 文件中的文件名
【发布时间】:2013-03-25 08:35:11
【问题描述】:

我尝试读取一个 gz 文件:

with open(os.path.join(storage_path,file), "rb") as gzipfile:
        with gzip.GzipFile(fileobj=gzipfile) as datafile:
            data = datafile.read()

它有效,但我需要包含在我的 gz 文件中的每个文件的文件名和大小。 此代码将包含文件的内容打印到存档中。

如何读取包含在这个 gz 文件中的文件名?

【问题讨论】:

  • gzip 只能压缩单个文件。你有 gzipped tar 存档吗?
  • 我有一个 gz 文件,但我需要知道存档中包含的文件及其大小
  • 在这个 gz 文件中我有一个 pcap 文件
  • 实际上并非如此,gzip 文件可以包含多个名为“members”的文件。请参阅规范的“文件格式”部分。

标签: python gzip compression


【解决方案1】:

Python gzip 模块不提供对该信息的访问。

源代码跳过它而不存储它:

if flag & FNAME:
    # Read and discard a null-terminated string containing the filename
    while True:
        s = self.fileobj.read(1)
        if not s or s=='\000':
            break

文件名组件是可选的,不保证存在(我认为在这种情况下,命令行gzip -c 解压缩选项将使用原始文件名sans .gz)。未压缩的文件大小不存储在标头中;你可以在最后四个字节中找到它。

要自己从标题中读取文件名,您需要重新创建文件标题读取代码,并保留文件名字节。以下函数返回加上解压后的大小:

import struct
from gzip import FEXTRA, FNAME

def read_gzip_info(gzipfile):
    gf = gzipfile.fileobj
    pos = gf.tell()

    # Read archive size
    gf.seek(-4, 2)
    size = struct.unpack('<I', gf.read())[0]

    gf.seek(0)
    magic = gf.read(2)
    if magic != '\037\213':
        raise IOError('Not a gzipped file')

    method, flag, mtime = struct.unpack("<BBIxx", gf.read(8))

    if not flag & FNAME:
        # Not stored in the header, use the filename sans .gz
        gf.seek(pos)
        fname = gzipfile.name
        if fname.endswith('.gz'):
            fname = fname[:-3]
        return fname, size

    if flag & FEXTRA:
        # Read & discard the extra field, if present
        gf.read(struct.unpack("<H", gf.read(2)))

    # Read a null-terminated string containing the filename
    fname = []
    while True:
        s = gf.read(1)
        if not s or s=='\000':
            break
        fname.append(s)

    gf.seek(pos)
    return ''.join(fname), size

将上述函数与已创建的gzip.GzipFile 对象一起使用:

filename, size = read_gzip_info(gzipfileobj)

【讨论】:

  • 未压缩的文件大小模 2^32 是“成员”的最后四个字节。
  • @PavelAnossov:是的,我刚才看到了你的回答。 :-)
  • @moose 是的;我现在已将其更新为与 Python 3 兼容的语法。很抱歉!
【解决方案2】:

GzipFile 本身没有这个信息,但是:

  1. 文件名(通常)是存档的名称减去.gz
  2. 如果未压缩文件小于 4G,则存档的最后四个字节包含未压缩大小:

 

In [14]: f = open('fuse-ext2-0.0.7.tar.gz')

In [15]: f.seek(-4, 2)

In [16]: import struct

In [17]: r = f.read()

In [18]: struct.unpack('<I', r)[0]
Out[18]: 7106560

In [19]: len(gzip.open('fuse-ext2-0.0.7.tar.gz').read())
Out[19]: 7106560

(从技术上讲,最后四个字节是原始(未压缩)输入数据模2的大小32(成员预告片中的ISIZE字段,http://www.gzip.org/zlib/rfc-gzip.html))

【讨论】:

  • 实际上并非如此。 gzip 文件可以包含原始文件名(参见规范中的 FNAME 标志)。
  • gzip 文件可以,但 GzipFile 类不公开它。见 Martijn 的回答,他必须自己解析标题。
  • 我明白了,我没有仔细阅读您的答案;我读到这一点,因为 gzip 文件规范没有相关信息。对于投反对票,我深表歉意。
【解决方案3】:

我已经在这个模式下解决了:

fl = search_files(storage_path)     
for f in fl:
    with open(os.path.join(storage_path,f), "rb") as gzipfile:
        with gzip.GzipFile(fileobj=gzipfile) as datafile:
            data = datafile.read()
        print str(storage_path) + "/" + str(f[:-3]) +  " : " + str(len(data)) + " bytes" #pcap file size

不知道对不对

有什么建议吗?

【讨论】:

  • 可行,但显然需要解压。如果您有很多大文件,这可能会变慢。
  • 好的,很好的观察!现在我尝试使用您之前发布的代码!谢谢
【解决方案4】:

新代码:

fl = search_files(storage_path)     
for f in fl:
    with open(os.path.join(storage_path,f), "rb") as gzipfile:
        #try with module 2^32
        gzipfile.seek(-4,2)
        r = gzipfile.read()
        print str(storage_path) + "/" + str(f[:-3]) +  " : " + str(struct.unpack('<I' ,r)[0]) + " bytes" #dimensione del file pcap

【讨论】:

  • 如果用户更改 gzip 文件的名称以及文件扩展名怎么办?
【解决方案5】:

Martjin的解决方案真不错,我已经为Python 3.6+打包了:https://github.com/PierreSelim/gzinfo

只需pip install gzinfo

在您的代码中

import gzinfo

info = gzinfo.read_gz_info('bar.txt.gz')

# info.name is 'foo.txt'
print(info.fname)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-22
    • 1970-01-01
    相关资源
    最近更新 更多