2009-09-02 5 views
2

Je ne suis pas sûr de faire cela correctement ou si ma logique est correcte.Comment supprimer des fichiers en traversant l'arborescence des dossiers

Je suis en train de descendre une structure de dossiers supprimer des fichiers âgés d'un certain nombre de jours, cette partie que j'ai correctement mis en œuvre, supprimer les dossiers vides.

Tout cela peut-il être fait en une seule boucle?
Où puis-je supprimer le dossier?

Je souhaite supprimer les dossiers vides jusqu'à 3 ou 4 niveaux.

private static void TraverseTree(System.IO.DirectoryInfo folder, double days) 
    { 
     Stack<string> dirs = new Stack<string>(); 

     if (!folder.Exists) 
      throw new ArgumentException(); 

     dirs.Push(folder.FullName); 

     while (dirs.Count > 0) 
     { 
      string currentDir = dirs.Pop(); 
      string[] subDirs; 
      try 
      { 
       subDirs = System.IO.Directory.GetDirectories(currentDir); 
      } 
      // An UnauthorizedAccessException exception will be thrown if we do not have 
      // discovery permission on a folder or file. It may or may not be acceptable 
      // to ignore the exception and continue enumerating the remaining files and 
      // folders. It is also possible (but unlikely) that a DirectoryNotFound exception 
      // will be raised. This will happen if currentDir has been deleted by 
      // another application or thread after our call to Directory.Exists. The 
      // choice of which exceptions to catch depends entirely on the specific task 
      // you are intending to perform and also on how much you know with certainty 
      // about the systems on which this code will run. 
      catch (UnauthorizedAccessException e) 
      { 
       Console.WriteLine(e.Message); 
       continue; 
      } 
      catch (System.IO.DirectoryNotFoundException e) 
      { 
       Console.WriteLine(e.Message); 
       continue; 
      } 

      string[] files = null; 
      try 
      { 
       files = System.IO.Directory.GetFiles(currentDir); 
      } 
      catch (UnauthorizedAccessException e) 
      { 

       Console.WriteLine(e.Message); 
       continue; 
      } 
      catch (System.IO.DirectoryNotFoundException e) 
      { 
       Console.WriteLine(e.Message); 
       continue; 
      } 

      // Perform the required action on each file here. 
      // Modify this block to perform your required task. 
      foreach (string file in files) 
      { 
       try 
       { 
        // Perform whatever action is required in your scenario. 
        System.IO.FileInfo fi = new System.IO.FileInfo(file); 
        Console.WriteLine("{0}: {1}, {2}", fi.Name, fi.Length, fi.CreationTime); 

        // Delete old files 
        if (fi.LastWriteTime < DateTime.Now.AddDays(-days)) 
         fi.Delete(); 
       } 
       catch (System.IO.FileNotFoundException e) 
       { 
        // If file was deleted by a separate application 
        // or thread since the call to TraverseTree() 
        // then just continue. 
        Console.WriteLine(e.Message); 
        continue; 
       } 
      } 

      // Push the subdirectories onto the stack for traversal. 
      // This could also be done before handing the files. 
      foreach (string str in subDirs) 
       dirs.Push(str); 
     } 
    } 

Le code provient de MSDN.

Répondre

5

Une approche récursive serait probablement plus propre.

private static void DeleteOldFilesAndFolders(string path) 
{ 
    foreach (string directory in System.IO.Directory.GetDirectories(path)) 
    { 
     DeleteOldFilesAndFolders(directory); 

     // If the directory is empty and old, delete it here. 
    } 

    foreach (string file in System.IO.Directory.GetFiles(path)) 
    { 
     // Check the file's age and delete it if it's old. 
    } 
} 
+0

Donnez-moi un système de fichiers récursive et je vais vous donner un système de fichiers qui débordera votre pile. – plinth

+0

@plinth Vraiment? Quelle est la profondeur de votre système de fichiers? Vous avez vraiment des fichiers enfouis des centaines de dossiers en profondeur? –

+0

Oui vraiment. Si vous définissez votre pile sur n, j'intégrerai n + 1 dossiers et vous donnerai un cas d'utilisation «raisonnable» pour expliquer pourquoi c'est ainsi. Il y a des années, j'ai travaillé sur Acrobat Catalog et nous avons dû le protéger contre ce genre de situations. L'allocation de tas est un peu plus difficile à mettre en œuvre, beaucoup plus difficile à épuiser et plus facile à piéger lorsque les ressources sont épuisées. Quelle est la meilleure solution dans le monde réel? – plinth

0

Here is a more general solution au problème qui vous donne un déambulateur système de fichiers mis en œuvre non récursive comme IEnumerable.

pour laquelle votre solution peut probablement être mis en œuvre:

List<string> directoriesToDelete = new List<string>(); 
DirectoryWalker walker = new DirectoryWalker(@"C:\pathToSource\src", 
    dir => { 
     if (Directory.GetFileSystemEntries(dir).Length == 0) { 
      directoriesToDelete.Add(dir); 
      return false; 
     } 
     return true; 
    }, 
    file => { 
     if (FileIsTooOld(file)) { 
      return true; 
     } 
     return false; 
    } 
    ); 
foreach (string file in walker) 
    File.Delete(file); 
foreach (string dir in directoriesToDelete) 
    Directory.Delete(dir); 
3

Quelque chose que je remarque au sujet de votre code est que les dizaines de lignes de « mécanisme » pour la marche de la structure arborescente submerge complètement les deux lignes de code qui fait le travail. Cela rend difficile la lecture, la compréhension, la modification, le débogage et la maintenance de ce code.

Voici ce que je ferais à la place.

Vous avez seulement trois opérations de haut niveau dans votre programme: (1) obtenir tous les fichiers, (2) filtrer pour trouver ceux à supprimer, (3) supprimer chaque fichier. Alors écrivez un programme qui fait chacun de ceux dans une déclaration. Pour la première opération, je factoriserais le mécanisme ci-dessus dans sa propre fonction: une fonction qui implémente, disons, IEnumerable, et tout ce qu'elle fait est de fournir des informations sur les fichiers. Il ne faire quoi que ce soit avec eux; son seul but est de continuer à cracher des informations sur les fichiers. Une fois que vous avez ce mécanisme, vous pouvez commencer à écrire des requêtes au-dessus de cette séquence pour atteindre la deuxième opération. La troisième opération découle alors directement de la seconde.

En bref, devrait ressembler à la ligne principale de votre programme ceci:

var allFiles = TraverseFolder(folder); 
var filesToDelete = from file in allFiles where IsOld(file) select file; 
foreach(var fileToDelete in filesToDelete) Delete(fileToDelete); 

Est-ce clair?

+0

+1 Oui, très clair. Merci – Picflight

0

I améliorée John's solution, la mise en œuvre du code manquant, la gestion des erreurs et des contrôles:

/* Given a directory path and a datetime, 
* recursively delete all files and directories contained in such directory 
* (given directory included) that are younger than the given date. 
*/ 
private bool DeleteDirectoryTree(string dir, DateTime keepFilesYoungerThan) 
{ 
    //checks 
    if (String.IsNullOrEmpty(dir) || !Directory.Exists(dir)) 
     return false; 

    //recurse on children directories 
    foreach (string childDir in Directory.GetDirectories(dir)) 
     DeleteDirectoryTree(childDir, keepFilesYoungerThan); 

    //loop on children files 
    foreach (string file in Directory.GetFiles(dir)) 
    { 
     //calculate file datetime 
     DateTime fileDT = new DateTime(Math.Max(File.GetCreationTime(file).Ticks, File.GetLastWriteTime(file).Ticks)); 
     //if file is old, delete it 
     if (fileDT <= keepFilesYoungerThan) 
      try 
      { 
       File.Delete(file); 
       Log("Deleted file " + file); 
      } 
      catch (Exception e) 
      { 
       LogError("Could not delete file " + file + ", cause: " + e.Message); 
      } 
    } 

    //if this directory is empty, delete it 
    if (!Directory.EnumerateFileSystemEntries(dir).Any()) 
     try 
     { 
      Directory.Delete(dir); 
      Log("Deleted directory " + dir); 
     } 
     catch (Exception e) 
     { 
      LogError("Could not delete directory " + dir + ", cause: " + e.Message); 
     } 

    return true; 
} 
Questions connexes