Come elencare ricorsivamente tutti i file in una directory in C #?


315

Come elencare ricorsivamente tutti i file in una directory e le directory secondarie in C #?


1
Dove vuoi popolare? se albero ... ecco l'esempio dreamincode.net/code/snippet2591.htm
Arsen Mkrtchyan,

77
string [] filenames = Directory.GetFiles (path, "*", SearchOption.AllDirectories)
Bruce,

Potresti voler esaminare questa domanda in cui ho presentato un esempio di codice che utilizza la ricorsione per eseguire il rendering di una struttura di directory in TreeView. La logica dovrebbe essere la stessa nella maggior parte dei casi.
Cerebrus,

5
Il problema è che si rompe molto facilmente se non si ha accesso a una singola directory: nessun risultato ...
Marc Gravell

1
Se
riscontri

Risposte:


186

Questo articolo copre tutto ciò di cui hai bisogno. Tranne che per cercare i file e confrontare i nomi, basta stampare i nomi.

Può essere modificato in questo modo:

static void DirSearch(string sDir)
{
    try
    {
        foreach (string d in Directory.GetDirectories(sDir))
        {
            foreach (string f in Directory.GetFiles(d))
            {
                Console.WriteLine(f);
            }
            DirSearch(d);
        }
    }
    catch (System.Exception excpt)
    {
        Console.WriteLine(excpt.Message);
    }
}

Aggiunto da barlop

GONeale menziona che quanto sopra non elenca i file nella directory corrente e suggerisce di mettere la parte dell'elenco dei file al di fuori della parte che ottiene le directory. Quanto segue lo farebbe. Include anche una linea Writeline che puoi rimuovere il commento, che aiuta a tracciare dove ti trovi nella ricorsione che può aiutare a mostrare le chiamate per mostrare come funziona la ricorsione.

            DirSearch_ex3("c:\\aaa");
            static void DirSearch_ex3(string sDir)
            {
                //Console.WriteLine("DirSearch..(" + sDir + ")");
                try
                {
                    Console.WriteLine(sDir);

                    foreach (string f in Directory.GetFiles(sDir))
                    {
                        Console.WriteLine(f);
                    }

                    foreach (string d in Directory.GetDirectories(sDir))
                    {
                        DirSearch_ex3(d);
                    }
                }
                catch (System.Exception excpt)
                {
                    Console.WriteLine(excpt.Message);
                }
            }

86
Questo metodo non elenca i file per la directory iniziale, ma solo le directory secondarie e inferiori. Spostavo GetFiles fuori GetDirectories
GONeale il

1
A volte non si vogliono i file per la directory iniziale, nel qual caso questo è perfetto per strutture ragionevolmente piccole. Per molto grandi liste, uso qualcosa come soluzione di Marc Gravell: stackoverflow.com/a/929418/91189
Joseph Gabriel

2
@GONeale è corretto. È molto meno plausibile per un utente non aspettarsi l'elenco dei file della directory radice di input. La parola inserita è la chiave qui. È stato inserito per un motivo.
Florin Mircea,

2
Ho dovuto aggiungere un tentativo di aggirare il ciclo foreach interno altrimenti non continua l'accesso negato errori
Shaun Vermaak

3
Dovresti evitare di rilevare Eccezione, ad esempio vorresti davvero prendere An OutOfMemoryException? Prendi solo ciò che riesci a gestire.
alastairtree,

435

Si noti che in .NET 4.0 sono incorporate (presumibilmente) funzioni di file basate su iteratore (anziché basate su array):

foreach (string file in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories))
{
    Console.WriteLine(file);
}

Al momento userei qualcosa di simile al di sotto; il metodo ricorsivo integrato si interrompe troppo facilmente se non si ha accesso a un singolo sub-dir ...; l' Queue<string>utilizzo evita troppe ricorsioni dello stack di chiamate e il blocco iteratore evita di avere un array enorme.

static void Main() {
    foreach (string file in GetFiles(SOME_PATH)) {
        Console.WriteLine(file);
    }
}

static IEnumerable<string> GetFiles(string path) {
    Queue<string> queue = new Queue<string>();
    queue.Enqueue(path);
    while (queue.Count > 0) {
        path = queue.Dequeue();
        try {
            foreach (string subDir in Directory.GetDirectories(path)) {
                queue.Enqueue(subDir);
            }
        }
        catch(Exception ex) {
            Console.Error.WriteLine(ex);
        }
        string[] files = null;
        try {
            files = Directory.GetFiles(path);
        }
        catch (Exception ex) {
            Console.Error.WriteLine(ex);
        }
        if (files != null) {
            for(int i = 0 ; i < files.Length ; i++) {
                yield return files[i];
            }
        }
    }
}

1
@soandos Sul punto di ricorsione ricorsivo EnumerateFiles genera IOException "Il nome del file non può essere risolto dal sistema"
SerG

5
Per tutti coloro che vogliono sapere se *.*include anche file senza estensione: Sì, lo fa, testato un minuto fa.
Tobias Knauss,

1
Per usarlo dovrai aggiungereusing System.IO;
Reinstate Monica - Arrivederci SE

7
@Wikis e da usare Consoledovrai aggiungere using System;- ma dal momento che l'IDE può aggiungere tutte le usingdirettive necessarie per te (ctrl +.), E dal momento che non stiamo usando nulla di esotico qui, è comune non includerle. Cavolo, avrai anche bisogno di una classdefinizione, ecc.

1
@MarcGravell Siamo nel core .net e nel mondo di Visual Studio Code ora, quindi includere le istruzioni Using è sempre il benvenuto in qualsiasi esempio di codice .net per salvare una serie di ricerche e inutili "
rasature di

98
Directory.GetFiles("C:\\", "*.*", SearchOption.AllDirectories)

2
Come evitare l'errore se l'utente di accesso non ha accesso ad alcune cartelle.
Romil Kumar Jain,

5
@Romil Non credo che questo frammento di codice stia cercando di indicare la piena funzionalità, solo la funzionalità grezza che l'OP stava cercando. Grazie per aver condiviso, Pescuma!
kayleeFrye_onDeck

@kayleeFrye_onDeck, ho posto solo un problema nel caso in cui vi sia un rilancio per una qualsiasi delle cartelle durante il recupero dei file. A causa di questa preoccupazione, implementiamo la nostra funzione ricorsiva personalizzata.
Romil Kumar Jain,

3
Riceverai "UnauthorizedAccessException" con questa soluzione. Dovresti avere una soluzione in grado di gestire errori come questo.
Kairan,

13

In .NET 4.5, almeno, c'è questa versione che è molto più breve e ha l'ulteriore vantaggio di valutare qualsiasi criterio di file per l'inclusione nell'elenco:

public static IEnumerable<string> GetAllFiles(string path, 
                                              Func<FileInfo, bool> checkFile = null)
{
    string mask = Path.GetFileName(path);
    if (string.IsNullOrEmpty(mask)) mask = "*.*";
    path = Path.GetDirectoryName(path);
    string[] files = Directory.GetFiles(path, mask, SearchOption.AllDirectories);

    foreach (string file in files)
    {
        if (checkFile == null || checkFile(new FileInfo(file)))
            yield return file;
    }
}

Usa così:

var list = GetAllFiles(mask, (info) => Path.GetExtension(info.Name) == ".html").ToList();

Questo non gestisce un caso in cui hai una directory vuota ... non c'è alcuna dichiarazione di ritorno all'interno della funzione.
FrumkinWY,

@FrumkinWY cosa succede con una directory vuota? Non ho una macchina a portata di mano per testarlo ora.
John Kaster,

12
IEnumerable<string> GetFilesFromDir(string dir) =>
 Directory.EnumerateFiles(dir).Concat(
 Directory.EnumerateDirectories(dir)
          .SelectMany(subdir => GetFilesFromDir(subdir)));

3

In Framework 2.0 puoi usare (Elenca i file della cartella principale, è la migliore risposta più popolare):

static void DirSearch(string dir)
{
    try
    {
        foreach (string f in Directory.GetFiles(dir))
            Console.WriteLine(f);
        foreach (string d in Directory.GetDirectories(dir))
        {
            Console.WriteLine(d);
            DirSearch(d);
        }

    }
    catch (System.Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

3

Alcune risposte eccellenti ma queste risposte non hanno risolto il mio problema.

Non appena si presenta un problema di autorizzazione della cartella: "Autorizzazione negata" il codice ha esito negativo. Questo è ciò che ho usato per aggirare il problema "Autorizzazione negata":

private int counter = 0;

    private string[] MyDirectories = Directory.GetDirectories("C:\\");

    private void ScanButton_Click(object sender, EventArgs e)
    {
        Thread MonitorSpeech = new Thread(() => ScanFiles());
        MonitorSpeech.Start();
    }

    private void ScanFiles()
    {
        string CurrentDirectory = string.Empty;

        while (counter < MyDirectories.Length)
        {
            try
            {
                GetDirectories();
                CurrentDirectory = MyDirectories[counter++];
            }
            catch
            {
                if (!this.IsDisposed)
                {
                    listBox1.Invoke((MethodInvoker)delegate { listBox1.Items.Add("Access Denied to : " + CurrentDirectory); });
                }
            }
        }
    }

    private void GetDirectories()
    {
        foreach (string directory in MyDirectories)
        {
            GetFiles(directory);
        }
    }

    private void GetFiles(string directory)
    {
        try
        {
            foreach (string file in Directory.GetFiles(directory, "*"))
            {
                listBox1.Invoke((MethodInvoker)delegate { listBox1.Items.Add(file); });
            }
        }
        catch
        {
            listBox1.Invoke((MethodInvoker)delegate { listBox1.Items.Add("Access Denied to : " + directory); });
        }
    }

Spero che questo aiuti gli altri.


3

Una soluzione semplice e pulita

/// <summary>
/// Scans a folder and all of its subfolders recursively, and updates the List of files
/// </summary>
/// <param name="sFullPath">Full path of the folder</param>
/// <param name="files">The list, where the output is expected</param>
internal static void EnumerateFiles(string sFullPath, List<FileInfo> fileInfoList)
{
    try
    {
        DirectoryInfo di = new DirectoryInfo(sFullPath);
        FileInfo[] files = di.GetFiles();

        foreach (FileInfo file in files)
            fileInfoList.Add(file);

        //Scan recursively
        DirectoryInfo[] dirs = di.GetDirectories();
        if (dirs == null || dirs.Length < 1)
            return;
        foreach (DirectoryInfo dir in dirs)
            EnumerateFiles(dir.FullName, fileInfoList);

    }
    catch (Exception ex)
    {
        Logger.Write("Exception in Helper.EnumerateFiles", ex);
    }
}

3
Stai facendo manualmente ciò che DirectoryInfo.GetFiles () farà per te fuori dalla scatola - basta usare il sovraccarico con SearchOption.AllDirectories e farà tutto da solo. Quindi questa è una soluzione complicata .
philw

2

Preferisco usare DirectoryInfo perché posso ottenere FileInfo, non solo stringhe.

        string baseFolder = @"C:\temp";
        DirectoryInfo di = new DirectoryInfo(baseFolder);

        string searchPattern = "*.xml";

        ICollection<FileInfo> matchingFileInfos = di.GetFiles(searchPattern, SearchOption.AllDirectories)
            .Select(x => x)
            .ToList();

Lo faccio nel caso in futuro avessi bisogno di filtri futuri..in base alle proprietà di FileInfo.

        string baseFolder = @"C:\temp";
        DirectoryInfo di = new DirectoryInfo(baseFolder);

        string searchPattern = "*.xml";

        ICollection<FileInfo> matchingFileInfos = di.GetFiles(searchPattern, SearchOption.AllDirectories)
            .Where(x => x.LastWriteTimeUtc < DateTimeOffset.Now)
            .Select(x => x)
            .ToList();

Posso anche ricorrere alle stringhe, se necessario. (e sono ancora a prova di futuro per i filtri / roba da clausole where.

        string baseFolder = @"C:\temp";
        DirectoryInfo di = new DirectoryInfo(baseFolder);

        string searchPattern = "*.xml";

        ICollection<string> matchingFileNames = di.GetFiles(searchPattern, SearchOption.AllDirectories)
            .Select(x => x.FullName)
            .ToList();

Si noti che " . " È un modello di ricerca valido se si desidera archiviare per estensione.


1
private void GetFiles(DirectoryInfo dir, ref List<FileInfo> files)
{
    try
    {
        files.AddRange(dir.GetFiles());
        DirectoryInfo[] dirs = dir.GetDirectories();
        foreach (var d in dirs)
        {
            GetFiles(d, ref files);
        }
    }
    catch (Exception e)
    {

    }
}

1
Perché il parametro filesè ref? Non c'è bisogno.
Massimiliano Kraus,

@MassimilianoKraus Direi che, sebbene non richiesto, chiarisce che il suo metodo cambierà filese non puoi più dare new List<FileInfo>()come parametro che sarebbe inutile. Potrebbe consentire qualche subottimizzazione ed evitare di creare un nuovo oggetto a meno che non sia richiesto.
Jeromej

@JeromeJ se sai cos'è OOP, sai che ogni volta che passi un oggetto a un metodo, quel metodo può cambiare le proprietà / i campi dell'oggetto. Quindi refnon rende nulla di più chiaro. Lo refscopo è quello di cambiare l'intero filespuntatore anche per il chiamante del metodo: è un'operazione pericolosa e qui non ce n'è bisogno: puoi semplicemente compilare l'Elenco, non è necessario puntarlo nuovamente su un altro Elenco su il mucchio. refdovrebbe essere usato solo in casi molto particolari; il più delle volte devi solo implementare le cose in un modo più paradigmatico funzionale.
Massimiliano Kraus,

1

Per evitare il UnauthorizedAccessException, io uso:

var files = GetFiles(@"C:\", "*.*", SearchOption.AllDirectories);
foreach (var file in files)
{
    Console.WriteLine($"{file}");
}

public static IEnumerable<string> GetFiles(string path, string searchPattern, SearchOption searchOption)
{
    var foldersToProcess = new List<string>()
    {
        path
    };

    while (foldersToProcess.Count > 0)
    {
        string folder = foldersToProcess[0];
        foldersToProcess.RemoveAt(0);

        if (searchOption.HasFlag(SearchOption.AllDirectories))
        {
            //get subfolders
            try
            {
                var subfolders = Directory.GetDirectories(folder);
                foldersToProcess.AddRange(subfolders);
            }
            catch (Exception ex)
            {
                //log if you're interested
            }
        }

        //get files
        var files = new List<string>();
        try
        {
            files = Directory.GetFiles(folder, searchPattern, SearchOption.TopDirectoryOnly).ToList();
        }
        catch (Exception ex)
        {
            //log if you're interested
        }

        foreach (var file in files)
        {
            yield return file;
        }
    }
}

1

Se hai solo bisogno di nomi di file e dal momento che non mi è piaciuta molto la maggior parte delle soluzioni qui (dal punto di vista delle funzionalità o della leggibilità), che ne dici di questo pigro?

private void Foo()
{
  var files = GetAllFiles("pathToADirectory");
  foreach (string file in files)
  {
      // Use can use Path.GetFileName() or similar to extract just the filename if needed
      // You can break early and it won't still browse your whole disk since it's a lazy one
  }
}

/// <exception cref="T:System.IO.DirectoryNotFoundException">The specified path is invalid (for example, it is on an unmapped drive).</exception>
/// <exception cref="T:System.UnauthorizedAccessException">The caller does not have the required permission.</exception>
/// <exception cref="T:System.IO.IOException"><paramref name="path" /> is a file name.-or-A network error has occurred.</exception>
/// <exception cref="T:System.IO.PathTooLongException">The specified path, file name, or both exceed the system-defined maximum length. For example, on Windows-based platforms, paths must be less than 248 characters and file names must be less than 260 characters.</exception>
/// <exception cref="T:System.ArgumentNullException"><paramref name="path" /> is null.</exception>
/// <exception cref="T:System.ArgumentException"><paramref name="path" /> is a zero-length string, contains only white space, or contains one or more invalid characters as defined by <see cref="F:System.IO.Path.InvalidPathChars" />.</exception>
[NotNull]
public static IEnumerable<string> GetAllFiles([NotNull] string directory)
{
  foreach (string file in Directory.GetFiles(directory))
  {
    yield return file; // includes the path
  }

  foreach (string subDir in Directory.GetDirectories(directory))
  {
    foreach (string subFile in GetAllFiles(subDir))
    {
      yield return subFile;
    }
  }
}

1

Record più breve

string files = Directory.GetFiles(@"your_path", "*.jpg", SearchOption.AllDirectories);

0

Ecco il mio punto di vista, basato su quello di Hernaldo, se hai bisogno di trovare file con nomi di un certo modello, come file XML che da qualche parte nel loro nome contengono una stringa particolare:

// call this like so: GetXMLFiles("Platypus", "C:\\");
public static List<string> GetXMLFiles(string fileType, string dir)
{
    string dirName = dir; 
    var fileNames = new List<String>();
    try
    {
        foreach (string f in Directory.GetFiles(dirName))
        {
            if ((f.Contains(fileType)) && (f.Contains(".XML")))
            {
                fileNames.Add(f);
            }
        }
        foreach (string d in Directory.GetDirectories(dirName))
        {
            GetXMLFiles(fileType, d);
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
    return fileNames;
}

0

Elenco di file e cartelle da modellare, implementazione personalizzata.
Questo crea un elenco completo di tutti i file e le cartelle a partire dalla directory iniziale.

public class DirOrFileModel
    {
        #region Private Members

        private string _name;
        private string _location;
        private EntryType _entryType;

        #endregion

        #region Bindings

        public string Name
        {
            get { return _name; }
            set
            {
                if (value == _name) return;
                _name = value;
            }
        }

        public string Location
        {
            get { return _location; }
            set
            {
                if (value == _location) return;
                _location = value;
            }
        }

        public EntryType EntryType
        {
            get { return _entryType; }
            set
            {
                if (value == _entryType) return;
                _entryType = value;
            }
        }

        public ObservableCollection<DirOrFileModel> Entries { get; set; }

        #endregion

        #region Constructor

        public DirOrFileModel()
        {
            Entries = new ObservableCollection<DirOrFileModel>();
        }

        #endregion
    }

    public enum EntryType
    {
        Directory = 0,
        File = 1
    }

Metodo:

 static DirOrFileModel DirSearch(DirOrFileModel startDir)
        {
            var currentDir = startDir;
            try
            {
                foreach (string d in Directory.GetDirectories(currentDir.Location))
                {
                    var newDir = new DirOrFileModel
                    {
                        EntryType = EntryType.Directory,
                        Location = d,
                        Name = Path.GetFileName(d)
                    };
                    currentDir.Entries.Add(newDir);

                    DirSearch(newDir);
                }

                foreach (string f in Directory.GetFiles(currentDir.Location))
                {
                    var newFile = new DirOrFileModel
                    {
                        EntryType = EntryType.File,
                        Location = f,
                        Name = Path.GetFileNameWithoutExtension(f)
                    };
                    currentDir.Entries.Add(newFile);
                }

            }
            catch (Exception excpt)
            {
                Console.WriteLine(excpt.Message);
            }
            return startDir;
        }

Uso:

var dir = new DirOrFileModel
            {
                Name = "C",
                Location = @"C:\",
                EntryType = EntryType.Directory
            };

            dir = DirSearch(dir);

0

Soluzione breve e semplice

string dir = @"D:\PATH";

DateTime from_date = DateTime.Now.Date;
DateTime to_date = DateTime.Now.Date.AddHours(23);
var files = Directory.EnumerateFiles(dir, "*.*",SearchOption.AllDirectories).Select(i=>new FileInfo(i))
.Where(file=>file.LastWriteTime >= from_date && file.LastWriteTime <= to_date);
foreach(var fl in files)
    Console.WriteLine(fl.FullName);

0

Questo mi ha aiutato a ottenere tutti i file in una directory e sottodirectory, può essere utile per qualcuno. [Ispirato dalle risposte sopra]

static void Main(string[] args)
    {
        try
        {
            var root = @"G:\logs";
            DirectorySearch(root);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        Console.ReadKey();
    }





public static void DirectorySearch(string root, bool isRootItrated = false)
{
    if (!isRootItrated)
    {
        var rootDirectoryFiles = Directory.GetFiles(root);
        foreach (var file in rootDirectoryFiles)
        {
            Console.WriteLine(file);
        } 
    }

    var subDirectories = Directory.GetDirectories(root);
    if (subDirectories?.Any() == true)
    {
        foreach (var directory in subDirectories)
        {
            var files = Directory.GetFiles(directory);
            foreach (var file in files)
            {
                Console.WriteLine(file);
            }
            DirectorySearch(directory, true);
        }
    }
}

0
var d = new DirectoryInfo(@"C:\logs");
var list = d.GetFiles("*.txt").Select(m => m.Name).ToList();

0

Qualche versione migliorata con max lvl per andare giù nella directory e opzione per escludere le cartelle:

using System;
using System.IO;

class MainClass {
  public static void Main (string[] args) {

    var dir = @"C:\directory\to\print";
    PrintDirectoryTree(dir, 2, new string[] {"folder3"});
  }


  public static void PrintDirectoryTree(string directory, int lvl, string[] excludedFolders = null, string lvlSeperator = "")
  {
    excludedFolders = excludedFolders ?? new string[0];

    foreach (string f in Directory.GetFiles(directory))
    {
        Console.WriteLine(lvlSeperator+Path.GetFileName(f));
    } 

    foreach (string d in Directory.GetDirectories(directory))
    {
        Console.WriteLine(lvlSeperator + "-" + Path.GetFileName(d));

        if(lvl > 0 && Array.IndexOf(excludedFolders, Path.GetFileName(d)) < 0)
        {
          PrintDirectoryTree(d, lvl-1, excludedFolders, lvlSeperator+"  ");
        }
    }
  }
}

directory di input:

-folder1
  file1.txt
  -folder2
    file2.txt
    -folder5
      file6.txt
  -folder3
    file3.txt
  -folder4
    file4.txt
    file5.txt

output della funzione (il contenuto della cartella5 è escluso a causa del limite di livello e il contenuto della cartella3 è escluso perché è nell'array delle cartelle escluse):

-folder1
  file1.txt
  -folder2
    file2.txt
    -folder5
  -folder3
  -folder4
    file4.txt
    file5.txt

-1

Ecco una versione del codice di B. Clay Shannon non statica per i file Excel:

class ExcelSearcher
{
    private List<string> _fileNames;

    public ExcelSearcher(List<string> filenames)
    {
        _fileNames = filenames;
    }
    public List<string> GetExcelFiles(string dir, List<string> filenames = null)
    {

        string dirName = dir;
        var dirNames = new List<string>();
        if (filenames != null)
        {
            _fileNames.Concat(filenames);
        }
        try
        {
            foreach (string f in Directory.GetFiles(dirName))
            {
                if (f.ToLower().EndsWith(".xls") || f.ToLower().EndsWith(".xlsx"))
                {
                    _fileNames.Add(f);
                }
            }
            dirNames = Directory.GetDirectories(dirName).ToList();
            foreach (string d in dirNames)
            {
                GetExcelFiles(d, _fileNames);
            }
        }
        catch (Exception ex)
        {
            //Bam
        }
        return _fileNames;
    }

-1

Una soluzione molto semplice, restituisce un elenco di file.

    public static List<string> AllFilesInFolder(string folder)
    {
        var result = new List<string>();

        foreach (string f in Directory.GetFiles(folder))
        {
            result.Add(f);
        }

        foreach (string d in Directory.GetDirectories(folder))
        {
            result.AddRange(AllFilesInFolder(d));
        }

        return result;
    }

-2
static void Main(string[] args)
        {
            string[] array1 = Directory.GetFiles(@"D:\");
            string[] array2 = System.IO.Directory.GetDirectories(@"D:\");
            Console.WriteLine("--- Files: ---");
            foreach (string name in array1)
            {
                Console.WriteLine(name);
            }
            foreach (string name in array2)
            {
                Console.WriteLine(name);
            }
                  Console.ReadLine();
        }

1
uhhh ... questo non è ricorsivo
mxmissile il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.