2012-10-24 195 views
18

压缩文件可以分为以下逻辑组:
a。您正在使用的操作系统(* ix,Win)等。
b。不同类型的压缩算法(即.zip,.Z,.bz2,.rar,.gzip)。至少从主要使用的压缩文件的标准列表中获得。
c。然后我们有焦油球机制 - 我认为没有压缩。但它更像一个串联。Python - 识别压缩文件类型和解压缩的机制

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

我需要做什么
如何识别文件类型(压缩类型),然后联合国对其进行压缩?


像:

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

etc 

因此,根本的问题是我们如何识别基于文件的压缩算法(假设扩展不提供或不正确的)?有没有什么具体的方式来做到这一点在Python中?

回答

26

This page有一个“魔术”文件签名列表。抓住你需要的,并把它们放在下面的字典中。然后我们需要一个将字典键与文件开头相匹配的函数。我写了一个建议,但它可以通过将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" 

这个解决方案应该是跨plattform,当然也是不依赖于文件扩展名,但它可能会产生假阳性与随机内容文件恰好开始与一些特定的魔法字节。

+0

这很好地标识了文件类型。但是,您应该返回通过打开文件并允许访问创建的对象。否则,您将最终再次测试文件类型以查看您应该处理的文件类型。这可以通过创建一个可以处理所有支持的文件类型的通用抽象来避免。该模式称为“工厂”。 – Ber

+0

您也可以使用此网站搜索您想要的签名:http://www.filesignatures.net/index.php –

+0

zip文件格式允许将任意数据附加到文件的开头,因此检查所有情况下,zip文件的幻数都不正确。 –

0

“a”是完全错误的。

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

您实际上需要检查文件中的数据是否与扩展名期望的数据匹配。 也看看magic byte

+0

使用选项(a),我只是指用python编写的代码解压缩Unix,必须在WIN中为相同的文件解压缩。任何具体的原因,我错了? –

+1

压缩算法是OS独立的。您可以在Unix中压缩文件,然后在WIndows上解压缩,然后将其发送到Mac并再次压缩,比较来自Unix的压缩文件和来自Mac的压缩文件,并且它们会稍微平等。 – alexandernst

+0

@kumar_m_kiran通常(很可能),您可以使用相同的python代码在使用python的操作系统上解压缩文件。你打算根据不同操作系统上解压所需的python代码(这是平台无关性带来的)与不同操作系统需要不同的python代码(这通常不是真实的)的(不正确的)理解来进行分类。但是,你用一句话来表达这个意思,这意味着别的东西,亚历山大就会纠正你。 – abc

3

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

查找给定文件的文件类型的基础知识是在文件中找到一个标识头文件,通常称为"magic sequence" or signature header,它标识某个文件属于某种类型。如果可以避免的话,通常不会使用其名称或扩展名。对于某些文件,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命令,它会做很多的工作适合你。您也可以使用内置工具来解压缩文件。如果你只是做一个简单的脚本,这个方法更简单,并且会给你更好的性能。

13

基于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 = '\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()'等。

+0

在get_compressed_file函数中,您正在执行cls(f),f是一个文件处理程序,而您的打开函数需要文件名......我将其修改为关闭f,并传递文件名。有没有更好的办法? – fransua

+0

我以前的评论可能与python版本有关...在python2中bz2.BZ2File只接受字符串 – fransua

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