28

压缩文件可以分为以下逻辑组
a. 您正在使用的操作系统(*ix、Win)等。
b.不同类型的压缩算法(即.zip、.Z、.bz2、.rar、.gzip)。至少来自最常用的压缩文件的标准列表。
C。然后我们有焦油球机制——我想那里没有压缩。但它更像是一个串联。

现在,如果我们开始处理上述一组压缩文件,
a. 选项 (a) 将由 python 处理,因为它是独立于平台的语言。
湾。选项(b)和(c)似乎有问题。

我需要什么我
如何识别文件类型(压缩类型)然后解压缩它们?


像:

fileType = getFileType(fileName)  
switch(fileType):  
case .rar:  unrar....
case .zip:  unzip....

etc  

所以根本的问题是我们如何根据文件识别压缩算法(假设没有提供扩展名或不正确)?在 python 中有没有特定的方法可以做到这一点?

4

7 回答 7

37

这个页面有一个“魔法”文件签名列表。抓住你需要的那些,把它们放在像下面这样的字典里。然后我们需要一个函数来匹配 dict 键和文件的开头。我写了一个建议,尽管它可以通过预处理来优化,magic_dict例如一个巨大的编译正则表达式。

magic_dict = {
    "\x1f\x8b\x08": "gz",
    "\x42\x5a\x68": "bz2",
    "\x50\x4b\x03\x04": "zip"
    }

max_len = max(len(x) for x in magic_dict)

def file_type(filename):
    with open(filename) as f:
        file_start = f.read(max_len)
    for magic, filetype in magic_dict.items():
        if file_start.startswith(magic):
            return filetype
    return "no match"

这个解决方案应该是跨平台的,当然不依赖于文件扩展名,但它可能会给随机内容的文件带来误报,这些文件恰好以某些特定的魔术字节开头。

于 2012-10-24T07:53:38.823 回答
17

根据lazer的回答和我的评论,我的意思是:

class CompressedFile (object):
    magic = None
    file_type = None
    mime_type = None
    proper_extension = None

    def __init__(self, f):
        # f is an open file or file like object
        self.f = f
        self.accessor = self.open()

    @classmethod
    def is_magic(self, data):
        return data.startswith(self.magic)

    def open(self):
        return None

import zipfile

class ZIPFile (CompressedFile):
    magic = '\x50\x4b\x03\x04'
    file_type = 'zip'
    mime_type = 'compressed/zip'

    def open(self):
        return zipfile.ZipFile(self.f)

import bz2

class BZ2File (CompressedFile):
    magic = '\x42\x5a\x68'
    file_type = 'bz2'
    mime_type = 'compressed/bz2'

    def open(self):
        return bz2.BZ2File(self.f)

import gzip

class GZFile (CompressedFile):
    magic = '\x1f\x8b\x08'
    file_type = 'gz'
    mime_type = 'compressed/gz'

    def open(self):
        return gzip.GzipFile(self.f)


# factory function to create a suitable instance for accessing files
def get_compressed_file(filename):
    with file(filename, 'rb') as f:
        start_of_file = f.read(1024)
        f.seek(0)
        for cls in (ZIPFile, BZ2File, GZFile):
            if cls.is_magic(start_of_file):
                return cls(f)

        return None

filename='test.zip'
cf = get_compressed_file(filename)
if cf is not None:
    print filename, 'is a', cf.mime_type, 'file'
    print cf.accessor

现在可以使用cf.accessor. 所有模块都提供了类似的方法,如“read()”、“write()”等。

于 2012-10-24T08:57:42.947 回答
4

这是一个复杂的问题,取决于许多因素:最重要的是您的解决方案需要具有多大的可移植性。

查找给定文件的文件类型背后的基础是在文件中找到一个标识头,通常称为“魔术序列”或签名头,它标识文件属于某种类型。如果可以避免,通常不使用其名称或扩展名。对于某些文件,Python 内置了这个。例如,处理.tar文件,可以使用tarfile模块,它有一个方便的is_tarfile方法。有一个类似的模块名为zipfile. 这些模块还可以让您在纯 Python 中提取文件。

例如:

f = file('myfile','r')
if zipfile.is_zipfile(f):
    zip = zipfile.ZipFile(f)
    zip.extractall('/dest/dir')
elif tarfile.is_tarfile(f):
    ...

如果您的解决方案仅适用于 Linux 或 OSX,那么还有可以file为您完成大量工作的命令。您还可以使用内置工具解压缩文件。如果你只是做一个简单的脚本,这个方法更简单,会给你更好的性能。

于 2012-10-24T07:32:36.970 回答
1

接受的解决方案看起来不错,但它不适用于 python-3,以下是使其工作的修改——使用二进制 I/O 而不是字符串:

magic_dict = {
    b"\x1f\x8b\x08": "gz",
    b"\x42\x5a\x68": "bz2",
    b"\x50\x4b\x03\x04": "zip"
    }
''' SKIP '''
    with open(filename, "rb") as f:
''' The rest is the same '''
于 2020-08-13T22:52:46.510 回答
0

“a”是完全错误的。

“b”很容易被错误地解释,因为“.zip”并不意味着该文件实际上是一个 zip 文件。它可能是带有 zip 扩展名的 JPEG(如果您愿意,出于混淆目的)。

您实际上需要检查文件中的数据是否与扩展名预期的数据相匹配。也看看魔术字节

于 2012-10-24T07:28:49.420 回答
0

如果练习只是为了标记文件而识别它,那么您有很多答案。如果您想解压缩存档,为什么不尝试捕获执行/错误?例如:

>>> tarfile.is_tarfile('lala.txt')
False
>>> zipfile.is_zipfile('lala.txt')
False
>>> with bz2.BZ2File('startup.bat','r') as f:
...    f.read()
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
IOError: invalid data stream
于 2012-10-24T09:17:50.277 回答
0

2019 年更新:
我正在寻找一种解决方案来检测 .csv 文件是否被 gzip 压缩。@Lauritz 给出的答案对我来说是错误的,我想这只是因为在过去 7 年中读取文件的方式发生了变化。

这个库非常适合我! https://pypi.org/project/filetype/

于 2019-11-28T01:05:35.817 回答