2010-07-24 24 views
0

J'essaye de développer un extracteur récursif. Le problème est, c'est Recursing Too Much (Evertime, il a trouvé un type d'archive) et de prendre un coup de performance.Récursivité Récurrence récursion --- Comment puis-je améliorer les performances? (Extraction récursive d'archive de Python)

Alors, comment puis-je améliorer ci-dessous le code?

Mon Idée 1:

Obtenez le 'Dict' de direcories d'abord, avec types.Filetypes de fichiers comme clés. Extrayez les types de fichiers. Quand une archive est trouvée Extraire seulement celui-là. Puis Régénérer Archive Dict à nouveau.

Mon Idée 2:

os.walk retourne Generator. Alors, y a-t-il quelque chose que je peux faire avec les générateurs? Je suis nouveau aux générateurs.

ici est le code actuel:

import os, magic 
m = magic.open(magic.MAGIC_NONE) 
m.load() 

archive_type = [ 'gzip compressed data', 
     '7-zip archive data', 
     'Zip archive data', 
     'bzip2 compressed data', 
     'tar archive', 
     'POSIX tar archive', 
     'POSIX tar archive (GNU)', 
     'RAR archive data', 
     'Microsoft Outlook email folder (>=2003)', 
     'Microsoft Outlook email folder'] 

def extractRecursive(path ,archives): 
    i=0 
    for dirpath, dirnames, filenames in os.walk(path): 
     for f in filenames: 
      fp = os.path.join(dirpath, f) 
      i+=1 
      print i 
      file_type = m.file(fp).split(",")[0] 
      if file_type in archives: 
       arcExtract(fp,file_type,path,True) 
       extractRecursive(path,archives) 
    return "Done" 



def arcExtract(file_path,file_type,extracted_path="/home/v3ss/Downloads/extracted",unlink=False): 
    import subprocess,shlex 


    if file_type in pst_types: 
     cmd = "readpst -o '%s' -S '%s'" % (extracted_path,file_path) 
    else: 
     cmd = "7z -y -r -o%s x '%s'" % (extracted_path,file_path) 

    print cmd 
    args= shlex.split(cmd) 
    print args 

    try: 
     sp = subprocess.Popen(args, shell = False, stdout = subprocess.PIPE, stderr = subprocess.PIPE) 
     out, err = sp.communicate() 
     print out, err 
     ret = sp.returncode 
    except OSError: 
     print "Error no %s Message %s" % (OSError.errno,OSError.message) 
     pass 

    if ret == 0: 
     if unlink==True: 
      os.unlink(file_path) 
     return "OK!" 
    else: 
     return "Failed" 
if __name__ == '__main__': 
    extractRecursive('Path/To/Archives' ,archive_type) 

Répondre

1

Si, comme il semble, vous voulez extraire les fichiers d'archive des chemins « ci-dessus » celui ils sont, en soi os.walk (dans son fonctionnement top-down normal) ne peut pas vous aider (car au moment où vous extrayez une archive dans un certain répertoire x, os.walk peut probablement, mais pas nécessairement, déjà considéré le répertoire x - donc seulement en ayant os .walk regarder le chemin entier encore et encore pouvez-vous obtenir tout le contenu). Sauf que je suis surpris que votre code se termine jamais, puisque les fichiers de type archive devraient continuer à être trouvés et extraits - je ne vois pas ce qui peut jamais mettre fin à la récursion. (Pour résoudre cela, il suffirait de conserver un ensemble de tous les chemins des fichiers de type archive que vous avez déjà extraits, pour éviter de les considérer de nouveau lorsque vous les rencontrerez à nouveau). De toute façon, la meilleure architecture serait de arcExtract de retourner une liste de tous les fichiers qu'il a extraits (en particulier leurs chemins de destination) - alors vous pourriez simplement prolonger une liste avec tous ces fichiers extraits pendant la boucle os.walk (pas de récursivité), puis continuez à boucler juste sur la liste (pas besoin de demander au système d'exploitation à propos des fichiers et des répertoires, en économisant beaucoup de temps sur cette opération) et en produisant une nouvelle liste similaire. Pas de récursivité, pas de redondance du travail. J'imagine que readpst et 7z sont capables de fournir de telles listes (peut-être sur leur sortie ou erreur standard, que vous venez d'afficher mais que vous ne traitez pas) sous une forme textuelle que vous pourriez analyser pour en faire une liste ...?

+0

Oui on se demande comment il peut cesser, mais il arrête à cause de cela: à arcExtract: si unlink == Vrai: os.unlink (file_path) qui supprime les archives après un archivage. Récursif est nécessaire Parce qu'il existe des archives, Inside Archives, Imagine tar.gz et tar.bz2, ils doivent tous être extrait de façon récursive. Aussi les fichiers PST contiennent des pièces jointes qui ont des archives, ils doivent tous être extraits. –

+0

Votre architecture suggérée est réalisable. Je vais essayer. Le seul problème est readpst ne pas afficher le chemin du dossier extrait. Je peux le forcer sur l'option -o "Path_To_Extract" mais il doit d'abord vérifier les noms de dossier existants pour éviter toute collision. –

+0

@ V3ss, merci de clarifier pourquoi il s'arrête (ie, dû à la suppression après désarchivage) et de voir que l'itération appropriée (si vous pouvez obtenir les chemins des fichiers extraits) peut en effet supprimer le besoin de récursion (malgré l'archi problème interne, que j'avais déjà envisagé) - bonne chance de faire readpst faire ce dont vous avez besoin (ça sonne comme un problème pour superuser.com ;-). –

1

Vous pouvez simplifier la méthode extractRecursive utiliser os.walk comme il doit être utilisé. os.walk lit déjà tous les sous-répertoires afin que votre récursivité ne soit pas nécessaire.

Retirez simplement l'appel récursif et il devrait fonctionner :)

def extractRecursive(path, archives, extracted_archives=None): 
    i = 0 
    if not extracted_archives: 
     extracted_archives = set() 

    for dirpath, dirnames, filenames in os.walk(path): 
     for f in filenames: 
      fp = os.path.join(dirpath, f) 
      i += 1 
      print i 
      file_type = m.file(fp).split(',')[0] 
      if file_type in archives and fp not in extracted_archives: 
       extracted_archives.add(fp) 
       extracted_in.add(dirpath) 
       arcExtract(fp, file_type, path, True) 

    for path in extracted_in: 
     extractRecursive(path, archives, extracted_archives) 

    return "Done" 
+0

Un appel récursif est nécessaire PARCE QU'il s'agit d'une extraction d'archive multiniveau. Par exemple, disons que nous avons 50 archives qui peuvent contenir des archives comme tar.gz et tar.bz2, qui est archive tar dans l'archive bz2, La fonction doit d'abord extraire tar de l'archive bz2, puis extraire l'archive tar à nouveau. et 7z est seulement le choix que je reçois (il ne fait pas automatiquement comme dans le tar). Voici ce qu'il fait, il extrait le bz2, puis recurse en boucle, pour trouver le fichier tar qu'il extrait à nouveau. qui prennent des performances supplémentaires en particulier lorsque le fichier est au milieu de la liste. –

+0

@ V3ss0n: Je vois. Dans ce cas, vous avez 2 options. 1. Ce qu'Alex Martelli a suggéré, une solution propre mais peut-être plus difficile. 2. une boucle d'exécution 1-n où vous continuez à fonctionner jusqu'à ce qu'aucune nouvelle archive ne soit trouvée.Cela vous obligerait à garder une trace des archives extraites, mais est assez facile à mettre en œuvre. – Wolph

+0

WoLpH, pouvez-vous clarifier plus sur votre 2ème idée? –