¿Cómo enumerar recursivamente todos los archivos en un directorio en C #?

315

¿Cómo enumerar recursivamente todos los archivos en un directorio y directorios secundarios en C #?

Jim McKeeth
fuente
1
¿Dónde quieres poblar? si árbol ... aquí está el ejemplo dreamincode.net/code/snippet2591.htm
Arsen Mkrtchyan
77
string [] filenames = Directory.GetFiles (ruta, "*", SearchOption.AllDirectories)
Bruce
Es posible que desee ver esta pregunta en la que he presentado una muestra de código que utiliza la recursividad para representar una estructura de directorio en un TreeView. La lógica debería ser la misma en la mayoría de los casos.
Cerebrus
55
El problema con esto es que se rompe muy fácilmente si no tiene acceso a un solo directorio: sin resultados ...
Marc Gravell
1
Si se encuentra con problemas cuando no se puede acceder a algunos archivos, consulte Excepción de lanzamiento de enumeración de archivos
CodesInChaos

Respuestas:

186

Este artículo cubre todo lo que necesitas. Excepto en lugar de buscar los archivos y comparar nombres, simplemente imprima los nombres.

Se puede modificar así:

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);
    }
}

Añadido por barlop

GONeale menciona que lo anterior no enumera los archivos en el directorio actual y sugiere colocar la parte de la lista de archivos fuera de la parte que obtiene los directorios. Lo siguiente haría eso. También incluye una línea de Writeline que puede descomentar, que ayuda a rastrear dónde se encuentra en la recursión que puede ayudar a mostrar las llamadas para ayudar a mostrar cómo funciona la recursividad.

            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);
                }
            }
John T
fuente
86
Este método no enumera los archivos para el directorio inicial, solo son subdirectorios y versiones inferiores. Me gustaría mover GetFiles fuera de GetDirectories
GONeale
1
A veces uno no quiere los archivos para el directorio inicial, en cuyo caso esto es perfecto para estructuras razonablemente pequeñas. Para listas muy grandes, use algo como la solución de Marc Gravell: stackoverflow.com/a/929418/91189
Joseph Gabriel el
2
@GONeale es correcto. Es mucho menos plausible que un usuario no espere la lista de archivos del directorio raíz de entrada. La palabra de entrada es clave aquí. Ha sido ingresado por una razón.
Florin Mircea
2
Tuve que agregar un intento de captura alrededor del bucle foreach interno, de lo contrario no continuará con los errores de acceso denegado
Shaun Vermaak
3
Debería evitar capturar una excepción: ¿realmente le gustaría atrapar una excepción OutOfMemoryException, por ejemplo? Solo atrapa lo que puedas manejar.
alastairtree
435

Tenga en cuenta que en .NET 4.0 hay (supuestamente) funciones de archivo basadas en iterador (en lugar de matriz) integradas:

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

Por el momento usaría algo como a continuación; el método recursivo incorporado se rompe demasiado fácilmente si no tiene acceso a un solo subdirectorio ...; el Queue<string>uso evita demasiada recursividad de pila de llamadas, y el bloque iterador evita que tengamos una gran matriz.

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];
            }
        }
    }
}
Marc Gravell
fuente
1
@soandos En el punto de reparación recursiva EnumerateFiles arroja IOException "El sistema no puede resolver el nombre del archivo"
SerG
55
Para todos los que quieran saber si *.*también incluye archivos sin extensión de archivo: Sí, sí, probado hace un minuto.
Tobias Knauss
1
Para usar esto, necesitará agregarusing System.IO;
Restablecer Monica - Goodbye SE
77
@Wikis y para usar Consolenecesitará agregar using System;, pero dado que el IDE puede agregar todas las usingdirectivas necesarias para usted (ctrl +.), Y dado que no estamos usando nada exótico aquí, es común no incluirlas. Diablos, también necesitarás una classdefinición, etc. Solo digo
Marc Gravell
1
@MarcGravell Estamos en el núcleo .NET y Visual Studio Código mundo ahora, para incluyendo el uso de declaraciones, siempre es bienvenido en cualquier ejemplo de código .NET para guardar una serie de búsquedas y sin sentido "de afeitar de yak"
JohnC
98
Directory.GetFiles("C:\\", "*.*", SearchOption.AllDirectories)
Pescuma
fuente
2
Cómo evitar el error si el usuario de inicio de sesión no tiene acceso en algunas de las carpetas.
Romil Kumar Jain
55
@Romil No creo que este fragmento de código intente indicar la funcionalidad completa, solo la funcionalidad sin procesar que buscaba el OP. Gracias por compartir, Pescuma!
kayleeFrye_onDeck
@kayleeFrye_onDeck, pongo solo una inquietud en caso de que haya una elevación para alguna de las carpetas mientras obtengo archivos. Debido a esta preocupación, implementamos nuestra función recursiva personalizada.
Romil Kumar Jain
3
Recibirá "UnauthorizedAccessException" con esta solución. Debería tener una solución que pueda manejar errores como este.
Kairan
13

En .NET 4.5, al menos, existe esta versión que es mucho más corta y tiene la ventaja adicional de evaluar cualquier criterio de archivo para su inclusión en la lista:

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;
    }
}

Usar así:

var list = GetAllFiles(mask, (info) => Path.GetExtension(info.Name) == ".html").ToList();
John Kaster
fuente
Esto no maneja un caso en el que tenga un directorio vacío ... no hay una declaración de retorno dentro de la función.
FrumkinWY
@FrumkinWY, ¿qué sucede con un directorio vacío? No tengo una máquina a mano para probar esto ahora.
John Kaster
12
IEnumerable<string> GetFilesFromDir(string dir) =>
 Directory.EnumerateFiles(dir).Concat(
 Directory.EnumerateDirectories(dir)
          .SelectMany(subdir => GetFilesFromDir(subdir)));
Raz Megrelidze
fuente
3

En Framework 2.0 puede usar (enumera los archivos de la carpeta raíz, es mejor la respuesta más popular):

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);
    }
}
Hernaldo González
fuente
3

Algunas respuestas excelentes, pero estas respuestas no resolvieron mi problema.

Tan pronto como surge un problema de permiso de carpeta: "Permiso denegado" el código falla. Esto es lo que solía solucionar el problema "Permiso denegado":

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); });
        }
    }

Espero que esto ayude a otros.

clavo oxidado
fuente
3

Una solución simple y limpia.

/// <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);
    }
}
Sunil Purushothaman
fuente
3
Está haciendo manualmente lo que DirectoryInfo.GetFiles () hará por usted de forma inmediata: simplemente use la sobrecarga con SearchOption.AllDirectories y se repetirá por sí solo. Entonces esa es una solución complicada .
philw
2

Prefiero usar DirectoryInfo porque puedo obtener FileInfo's, no solo cadenas.

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

        string searchPattern = "*.xml";

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

Hago esto en caso de que en el futuro necesite un filtrado futuro ... basado en las propiedades de 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();

También puedo recurrir a cadenas si es necesario. (y todavía estoy preparado para el futuro para filtros / cosas de cláusula 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();

Tenga en cuenta que " . " Es un patrón de búsqueda válido si desea archivar por extensión.

granadaCoder
fuente
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)
    {

    }
}
psdehr
fuente
1
¿Por qué el parámetro fileses ref? No hay necesidad.
Massimiliano Kraus
@MassimilianoKraus Yo diría que, aunque no es obligatorio, deja en claro que su método se alterará filesy que ya no se puede dar new List<FileInfo>()como parámetro, lo que sería inútil. Podría permitir alguna suboptimización y evitar crear un nuevo objeto a menos que sea necesario.
jeromej
@JeromeJ si sabe qué es OOP, sabe que cada vez que pasa un objeto a un método, ese método puede cambiar las propiedades / campos del objeto. Entonces refno aclara nada. El refpropósito es cambiar todo el filespuntero incluso para la persona que llama al método: es una operación peligrosa y aquí no hay necesidad de eso: simplemente puede llenar la Lista, no necesita volver a apuntarla a otra Lista en el montón refdebe usarse solo en casos muy particulares; la mayoría de las veces solo necesitas implementar las cosas de una manera más paradigmática funcional.
Massimiliano Kraus
1

Para evitar UnauthorizedAccessException, yo 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;
        }
    }
}
Fidel
fuente
1

Si solo necesita nombres de archivo y dado que realmente no me gustaron la mayoría de las soluciones aquí (características o legibilidad), ¿qué tal esta perezosa?

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;
    }
  }
}
jeromej
fuente
1

Registro más corto

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

Aquí está mi ángulo, basado en Hernaldo, si necesita encontrar archivos con nombres de cierto patrón, como archivos XML que en algún lugar de su nombre contienen una cadena particular:

// 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;
}
B. Clay Shannon
fuente
0

Listado de archivos y carpetas para modelar, implementación personalizada.
Esto crea una lista completa de todos los archivos y carpetas a partir de su directorio de inicio.

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
    }

Método:

 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);
c_wiz_kid
fuente
0

Solución corta y simple

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);
Nitin Sawant
fuente
0

Este me ayudó a obtener todos los archivos en un directorio y subdirectorios. Puede ser útil para alguien. [Inspirado en las respuestas anteriores]

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);
        }
    }
}
Akbar Badhusha
fuente
0
var d = new DirectoryInfo(@"C:\logs");
var list = d.GetFiles("*.txt").Select(m => m.Name).ToList();
Sameera R.
fuente
0

Algunas versiones mejoradas con max lvl para bajar en el directorio y la opción de excluir carpetas:

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+"  ");
        }
    }
  }
}

directorio de entrada:

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

salida de la función (el contenido de la carpeta5 se excluye debido al límite de lvl y el contenido de la carpeta3 se excluye porque está en el conjunto de carpetas excluidas):

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

Aquí hay una versión del código de B. Clay Shannon no estático para archivos de 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;
    }
Sardoan
fuente
-1

Una solución muy simple, devuelve una lista de archivos.

    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;
    }
jamie yello
fuente
-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();
        }
Goutham Padmanabha
fuente
1
uhhh ... esto no es recursivo
mxmissile