【问题标题】:How to tell if a file is gzip compressed?如何判断文件是否经过 gzip 压缩?
【发布时间】:2010-09-13 18:27:06
【问题描述】:

我有一个 Python 程序,它将文本文件作为输入。但是,其中一些文件可能是 gzip 压缩的。

是否有一种跨平台、可从 Python 中使用的方法来确定文件是否经过 gzip 压缩?

以下内容是否可靠,或者一个普通的文本文件是否“意外”看起来像 gzip 足以让我得到误报?

try:
    gzip.GzipFile(filename, 'r')
    # compressed
    # ...
except:
    # not compressed
    # ...

【问题讨论】:

  • 只是一个小提示......永远不要依赖文件结尾。请参阅 hop 的答案以了解如何操作。
  • @Helper:我不确定(请参阅我的编辑)。你仍然需要处理可能的 IOError,但是在我看来,没有后缀的 gzip 文件被破坏了......艰难的电话:)

标签: python compression gzip


【解决方案1】:

是否有一种跨平台、可从 Python 中使用的方法来确定文件是否经过 gzip 压缩?

accepted answer 解释了一般如何检测 gzip 压缩文件:测试前两个字节是否为 1f 8b。但是它没有说明如何在 Python 中实现它。

这是一种方法:

def is_gz_file(filepath):
    with open(filepath, 'rb') as test_f:
        return test_f.read(2) == b'\x1f\x8b'

【讨论】:

  • 不用binascii也可以:test_f.read(2) == b'\x1f\x8b'
  • 为了降低误报率,可以测试前三个字节为1f 8b 08
  • 如果文件不是.gz文件,那么test_f.read(2)不首先抛出OSError吗?是否还需要使用test_f.read(2) == b'\x1f\x8b' 检查字节?编辑:似乎这仅在 python 3.7 之后才可用。
【解决方案2】:

测试 gzip 文件的magic number 是唯一可靠的方法。但是,从 python3.7 开始,没有必要再自己比较字节了。 gzip 模块将为您比较字节,如果不匹配则引发异常!

从 python3.7 开始,这是可行的

import gzip
with gzip.open(input_file, 'r') as fh:
    try:
        fh.read(1)
    except OSError:
        print('input_file is not a valid gzip file by OSError')

从 python3.8 开始,这也有效:

import gzip
with gzip.open(input_file, 'r') as fh:
    try:
        fh.read(1)
    except gzip.BadGzipFile:
        print('input_file is not a valid gzip file by BadGzipFile')

【讨论】:

    【解决方案3】:

    gzip 压缩文件的magic number1f 8b。尽管对此进行测试并非 100% 可靠,但“普通文本文件”不太可能以这两个字节开头——在 UTF-8 中甚至不合法。

    通常 gzip 压缩文件带有后缀 .gz。即使gzip(1) 本身也不会在没有它的情况下解压文件,除非您使用--force 它。您可以使用它,但您仍然必须处理可能的 IOError(无论如何您都必须这样做)。

    你的方法的一个问题是,如果你给它一个未压缩的文件,gzip.GzipFile() 不会抛出异常。只有以后的read() 会。这意味着,您可能必须两次实现某些程序逻辑。丑。

    【讨论】:

    • gzip 压缩文件通常具有 .gz 文件扩展名(事实上,我认为我从未见过 .gzip 扩展名),但依靠文件扩展名进行测试通常是不安全的无论如何文件的类型。
    • 是吗? - gzip C 库将透明地读取未压缩的文件。虽然它会写入未压缩的文件,但它会将 CRC 代码通过它们以允许“gzip -t”(抓住我一次)
    • @Martin:确实如此:$ gunzip foo gzip: foo: unknown suffix -- 忽略
    • c 'library' gzip,即gzopen/gzread/etc 将透明地读取未压缩的文件。他们有一个开放的 compression=none 模式,它不会写入未更改的平面文件。
    • 关于扩展。您还必须检查相对常见的 .tgz 扩展名。
    【解决方案4】:

    gzip 本身会引发 OSError 如果它不是 gzip 压缩文件。

    >>> with gzip.open('README.md', 'rb') as f:
    ...     f.read()
    ...
    Traceback (most recent call last):
      File "<stdin>", line 2, in <module>
      File "/Users/dennis/.asdf/installs/python/3.6.6/lib/python3.6/gzip.py", line 276, in read
        return self._buffer.read(size)
      File "/Users/dennis/.asdf/installs/python/3.6.6/lib/python3.6/gzip.py", line 463, in read
        if not self._read_gzip_header():
      File "/Users/dennis/.asdf/installs/python/3.6.6/lib/python3.6/gzip.py", line 411, in _read_gzip_header
        raise OSError('Not a gzipped file (%r)' % magic)
    OSError: Not a gzipped file (b'# ')
    

    可以将此方法与其他方法结合使用以增加信心,例如检查 mimetype 或在文件头中查找幻数(请参阅其他答案以获取示例)并检查扩展名。

    import pathlib
    
    if '.gz' in pathlib.Path(filepath).suffixes:
       # some more inexpensive checks until confident we can attempt to decompress
       # ...
       try ...
         ...
       except OSError as e:
         ...
    

    【讨论】:

    • python 3.8 现在为此添加了一个更具体的错误,称为gzip.BadGzipFile。这个错误仍然继承自OSError
    【解决方案5】:

    导入mimetypes 模块。 它可以自动猜测你有什么样的文件,以及它是否被压缩。

    mimetypes.guess_type('blabla.txt.gz')
    

    返回:

    ('text/plain', 'gzip')

    【讨论】:

    • mimetypes 只检查文件名的结尾,实际上并不根据文件的内容进行猜测。
    【解决方案6】:

    在 python3 中似乎不太好用……

    import mimetypes
    filename = "./datasets/test"
    
    def file_type(filename):
        type = mimetypes.guess_type(filename)
        return type
    print(file_type(filename))
    

    返回(无,无) 但是从unix命令“文件”

    :~> 文件数据集/测试 数据集/测试:gzip 压缩数据,是“iostat_collection”,来自 Unix,最后修改时间:2015 年 1 月 29 日星期四 07:09:34

    【讨论】:

    • mimetypes 使用突出文件名来猜测类型。要从原始文件中检测文件类型,您需要使用“魔术”模块。
    猜你喜欢
    • 2011-08-28
    • 1970-01-01
    • 2011-04-26
    • 2011-09-24
    • 2017-11-07
    • 2022-01-09
    • 2017-02-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多