2012-10-24 147 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