2012-10-24 7 views
18

Un file compresso può essere classificato in sotto gruppi logici
a. Il sistema operativo su cui si sta lavorando (* ix, Win) ecc.
b. Diversi tipi di algoritmo di compressione (i.e .zip, .Z, .bz2, .rar, .gzip). Atleast da una lista standard di file compressi per lo più usati.
c. Poi abbiamo il meccanismo della palla di catrame - dove suppongo non ci sia compressione. Ma agisce più come una concatenazione.Python - meccanismo per identificare il tipo di file compresso e il uncompress

Ora, se iniziamo ad affrontare il set sopra di file compressi,
a. L'opzione (a) sarebbe curata da python poiché è un linguaggio indipendente dalla piattaforma.
b. L'opzione (b) e (c) sembra avere un problema.

cosa ho bisogno
Come identificare il tipo di file (tipo di compressione) e poi li UN-comprimere?


come:

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

etc 

Quindi la domanda fondamentale è come si fa a identificare l'algoritmo di compressione in base al file (supponendo che l'estensione non è previsto o non corretto)? C'è qualche modo specifico per farlo in Python?

risposta

26

This page ha un elenco di firme di file "magiche". Prendi quelli di cui hai bisogno e mettili in una parola come sotto. Quindi abbiamo bisogno di una funzione che corrisponda ai tasti dict con l'inizio del file.Ho scritto un suggerimento, sebbene possa essere ottimizzato preelaborando lo magic_dict in es. un gigante compilato regexp.

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" 

Questa soluzione deve essere cross-plattform ed è, naturalmente, non dipende dalla estensione del nome del file, ma può dare falsi positivi per i file con contenuti casuali che capita di iniziare con alcuni specifici byte magici.

+0

Identifica bene il tipo di file. Tuttavia, si dovrebbe tornare in oggetto creato aprendo il file e consentendo l'accesso. Altrimenti finirai per testare di nuovo il tipo di file per vederti che dovrebbe essere gestito. Questo può essere evitato creando un'astrazione comune in grado di gestire tutti i tipi di file supportati. Lo schema è chiamato "factory". – Ber

+0

Puoi anche utilizzare questo sito per cercare le firme che desideri: http://www.filesignatures.net/index.php –

+0

Il formato del file zip consente di aggiungere dati arbitrari all'inizio del file, quindi cerca un il numero magico per i file zip non è corretto in tutti i casi. –

0

"a" è completamente falso.

"b" può essere interpretato facilmente male, come ".zip" non significa che il file è in realtà un file zip. Potrebbe essere un JPEG con estensione zip (per scopi confusi, se lo si desidera).

In realtà è necessario verificare se i dati all'interno del file corrispondono ai dati previsti per l'estensione. Dai anche un'occhiata a magic byte.

+0

Con l'opzione (a), volevo dire solo il codice scritto in python per non-impacco dire Unix, devono lavorare per lo stesso file di non-compressione WIN. Qualche specifico motivo per cui ho torto? –

+1

Un algoritmo di compressione è indipendente dal sistema operativo. Puoi comprimere un file in Unix, quindi decomprimerlo su Windows, quindi inviarlo a un Mac e comprimerlo di nuovo, confrontare il file compresso da Unix e quello da Mac e saranno uguali bit a bit. – alexandernst

+0

@kumar_m_kiran Generalmente (molto probabilmente) è possibile utilizzare lo stesso codice Python per decomprimere un file attraverso i sistemi operativi usando python. Volevi classificare in base al codice Python necessario per decomprimere su diversi SO (che è ciò che porta l'indipendenza della piattaforma) con la (errata) comprensione che i diversi SO necessiteranno di un codice Python diverso (che in generale non è vero). Ma tu l'hai detto con una scelta di parole che significa qualcos'altro e alexandernst ti ha corretto. – abc

3

Questa è una domanda complessa che dipende da una serie di fattori: il più importante è quanto portatile deve essere la soluzione.

Le basi alla ricerca del tipo di file assegnato a un file consiste nel trovare un'intestazione di identificazione nel file, solitamente denominata "magic sequence" or signature header, che identifica un file di un determinato tipo. Il suo nome o estensione di solito non viene utilizzato se può essere evitato. Per alcuni file, Python ha questo built-in. Ad esempio, per gestire i file .tar, è possibile utilizzare il modulo tarfile, che dispone di un comodo metodo is_tarfile. C'è un modulo simile denominato zipfile. Questi moduli ti permetteranno di estrarre i file in Python puro.

Ad esempio:

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

Se la soluzione è Linux o OSX solo, c'è anche il comando file che farà un sacco di lavoro per voi. Puoi anche usare gli strumenti integrati per decomprimere i file. Se stai semplicemente facendo uno script semplice, questo metodo è più semplice e ti offrirà prestazioni migliori.

13

in base alla risposta del lazyr e il mio commento, qui è quello che voglio dire:

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 

ora possibile accedere ai dati compressi usando cf.accessor. Tutti i moduli forniscono metodi simili come "read()", "write()", ecc. Per fare ciò.

+0

nella funzione get_compressed_file che stai facendo cls (f), f è un gestore di file, mentre le tue funzioni aperte sono in attesa di nomi di file ... L'ho modificato per chiudere f e passare invece il nomefile. C'è un modo migliore? – fransua

+0

il mio commento precedente potrebbe essere correlato alla versione python ... in python2 bz2.BZ2File accetta solo stringhe – fransua

0

Se l'esercizio è quello di identificarlo solo per etichettare i file, si hanno molte risposte. Se vuoi decomprimere l'archivio, perché non provi a catturare le esecuzioni/errori? Per esempio:

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