2010-11-21 36 views
3

Je veux indexer tous mes fichiers musicaux et les stocker dans une base de données. J'ai cette fonction que j'appelle recusively, en commençant par la racine de mon lecteur de musique.C# Comment boucler un grand nombre de dossiers et de fichiers récursivement sans utiliser une énorme quantité de mémoire

à savoir

start > ReadFiles(C:\music\); 

ReadFiles(path){ 
    foreach(file) 
     save to index; 

    foreach(directory) 
     ReadFiles(directory); 
} 

Cela fonctionne très bien, mais lors de l'exécution du programme de la quantité de mémoire utilisée croît et se développe et .. enfin mon système à court de mémoire.

Quelqu'un at-il une meilleure approche qui n'a pas besoin de 4 Go de RAM pour effectuer cette tâche?

Cordialement, Tys

+4

Veuillez poster le code actuel. Il n'y a rien de fondamentalement faux dans votre approche. –

+3

La récursivité n'est pas limitée par la quantité de mémoire disponible, mais par la taille de la pile, donc si vous manquez de mémoire, il semble que vous conserviez les données trop longtemps. –

+0

Je ne peux pas imaginer que vous avez assez de musique pour exiger beaucoup d'espace. Etes-vous sûr de ne pas avoir un débordement de pile ou d'avoir entré une boucle sans fin à un moment donné? –

Répondre

9

La solution basée sur la file d'attente d'Alxandr devrait fonctionner correctement.

Si vous utilisez .NET 4.0, vous pouvez également profiter de la nouvelle méthode Directory.EnumerateFiles, qui énumère les fichiers paresseusement, sans les charger tous en mémoire:

void ReadFiles(string path) 
{ 
    IEnumerable<string> files = 
     Directory.EnumerateFiles(
      path, 
      "*", 
      SearchOption.AllDirectories); // search recursively 

    foreach(string file in files) 
     SaveToIndex(file); 
} 
+0

Nice. Je ne savais pas à ce sujet :-) – Alxandr

+0

Ceci est une bonne API à utiliser pour cette tâche. – Brian

+2

Ceci est une bonne réponse, mais cela ne résout pas le problème fondamental - quelque chose doit retenir trop de mémoire si vous avez des problèmes de mémoire. – configurator

1

Vous pouvez implémenter cela comme une file d'attente. Je pense (mais je ne suis pas sûr) que cela permettra d'économiser de la mémoire. Au moins, cela va libérer votre pile. Chaque fois que vous trouvez un dossier, vous l'ajoutez à la file d'attente, et chaque fois que vous trouvez un fichier, vous le lisez. Cela empêche la récursivité.

Quelque chose comme ceci:

Queue<string> dirs = new Queue<string>(); 
dirs.Enqueue("basedir"); 
while(dirs.Count > 0) { 
    foreach(directory) 
     dirs.Enqueue(directory); 
    ReadFiles(); 
} 
+1

Cela ne sauvera pas la mémoire. Par défaut, la pile est seulement 1 mégaoctet avant StackOverflow. S'il sort OutOfMemory, il y a un problème différent. – Brian

+0

@Brian: c'est toujours une amélioration par rapport au code d'origine ... –

+0

Merci pour toutes les réponses. Tout a aidé un peu. J'ai mis en place un mécanisme de file d'attente, fait des vérifications supplémentaires pour les répertoires qui ne devraient pas être indexés et, ce faisant, j'ai trouvé que mon NHibernate avait aussi besoin de réglages fins. Maintenant indexer plus de 1 TB avec facilité. – Tys

2

Avez-vous vérifié les entrées . et .. qui apparaissent dans chaque répertoire, sauf la racine?

Si vous ne les ignorez pas, vous aurez une boucle infinie.

+1

Cependant, ils n'apparaissent pas dans 'Directory.GetFiles' ou' Directory.GetDirectories'. Normalement, vous ne devriez pas courir dans ceci en travaillant dans. Net. – configurator

0

Prenez garde, cependant, que EnumerateFiles () s'arrêtera de fonctionner si vous n'avez pas accès à un fichier ou si un chemin est trop long ou si une autre exception se produit. Voilà ce que je l'utilise pour le moment pour résoudre ces problèmes:

public static List<string> getFiles(string path, List<string> files) 
{ 
    IEnumerable<string> fileInfo = null; 
    IEnumerable<string> folderInfo = null; 
    try 
    { 
     fileInfo = Directory.EnumerateFiles(str); 
    } 
    catch 
    { 

    } 
    if (fileInfo != null) 
    { 
     files.AddRange(fileInfo); 
     //recurse through the subfolders 
     fileInfo = Directory.EnumerateDirectories(str); 
     foreach (string s in folderInfo) 
     { 
      try 
      { 
       getFiles(s, files); 
      } 
      catch 
      { 

      } 
     } 
    } 
    return files; 
} 

Exemple d'utilisation:

List<string> files = new List<string>(); 
files = folder.getFiles(path, files); 

Ma solution est basée sur le code à cette page: http://msdn.microsoft.com/en-us/library/vstudio/bb513869.aspx.

Mise à jour: Une méthode beaucoup plus rapide pour obtenir des fichiers de manière récursive peut être trouvée à http://social.msdn.microsoft.com/Forums/vstudio/en-US/ae61e5a6-97f9-4eaa-9f1a-856541c6dcce/directorygetfiles-gives-me-access-denied?forum=csharpgeneral. Utiliser Stack est nouveau pour moi (je ne savais même pas qu'il existait), mais la méthode semble fonctionner. Au moins, il répertoriait tous les fichiers sur ma partition C et D sans erreurs.