Python-识别压缩文件类型和解压缩的机制



可以将压缩文件分为下面的逻辑组
A。您正在使用的操作系统(*ix,Win)等。
b。不同类型的压缩算法(即.zip,.z,.bz2,.rar,.gzip)。从大多数使用压缩文件的标准列表中至少。
C。然后我们有焦油球机构 - 我认为没有压缩。但这更像是一个串联。

现在,如果我们开始解决上述压缩文件集,
A。选项(a)将由Python照顾,因为它是平台独立语言。
b。选项(b)和(c)似乎有问题。

我需要什么
如何识别文件类型(压缩类型),然后取消压缩?


喜欢:

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

因此,基本问题是如何根据文件确定压缩算法(假设未提供或不正确)?在Python中有什么特定的方法?

此页面具有"魔术"文件签名的列表。抓住您需要的那些,然后将它们放在下面的命令中。然后,我们需要一个将DICT键与文件启动匹配的函数。我已经写了一个建议,尽管可以通过将magic_dict进行预处理进行优化。一个巨人汇编的言论。

magic_dict = {
    "x1fx8bx08": "gz",
    "x42x5ax68": "bz2",
    "x50x4bx03x04": "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"

此解决方案应为跨平台,并且当然不依赖文件名扩展名,但是它可能会给带有随机内容的文件提供误报,而随机内容恰好以某些特定的魔术字节开始。

基于Lazyr的回答和我的评论,这是我的意思:

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 = 'x50x4bx03x04'
    file_type = 'zip'
    mime_type = 'compressed/zip'
    def open(self):
        return zipfile.ZipFile(self.f)
import bz2
class BZ2File (CompressedFile):
    magic = 'x42x5ax68'
    file_type = 'bz2'
    mime_type = 'compressed/bz2'
    def open(self):
        return bz2.BZ2File(self.f)
import gzip
class GZFile (CompressedFile):
    magic = 'x1fx8bx08'
    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()"等。

这是一个复杂的问题,取决于许多因素:最重要的是您的解决方案的便携性。

找到给定文件的文件类型的基础知识是在文件中找到一个识别标头,通常是称为"魔术序列"或签名标头的内容,该标题标识了文件是某种类型的内容。如果可以避免,通常不使用其名称或扩展名。对于某些文件,Python具有内置。例如,要处理.tar文件,您可以使用具有方便的is_tarfile方法的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命令,它将为您完成很多工作。您也可以使用内置工具来解压缩文件。如果您只是做一个简单的脚本,此方法更简单,并且会给您更好的性能。

可接受的解决方案看起来很棒,但是与Python-3无法使用,以下是使其正常工作的修改 - 使用二进制I/O而不是字符串:

magic_dict = {
    b"x1fx8bx08": "gz",
    b"x42x5ax68": "bz2",
    b"x50x4bx03x04": "zip"
    }
''' SKIP '''
    with open(filename, "rb") as f:
''' The rest is the same '''

" a"是完全错误的。

" b"可以很容易地解释,因为" .zip"并不意味着该文件实际上是zip文件。它可能是带有Zip扩展的JPEG(如果需要的话,出于混乱的目的)。

实际上,您需要检查文件中的数据是否与预期的数据匹配。还要查看魔术字节。

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

>>> 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

2019更新:
我正在寻找一种解决方案来检测.CSV文件是否被gzze。@lauritz给出的答案是为我丢掉错误,我想这仅仅是因为在过去的7年中读取的文件的变化方式。

这个图书馆对我来说非常有效!https://pypi.org/project/filetype/

最新更新