Como listar recursivamente todos os arquivos em um diretório em c #?


315

Como listar recursivamente todos os arquivos em um diretório e diretórios filho em C #?


1
Onde você deseja preencher? se árvore ... aqui está o exemplo dreamincode.net/code/snippet2591.htm
Arsen Mkrtchyan

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

Você pode examinar esta questão em que apresentei um exemplo de código que usa recursão para renderizar uma estrutura de diretórios em um TreeView. A lógica deve ser a mesma na maioria dos casos.
Cerebrus

5
O problema é que ele se rompe com muita facilidade se você não tiver acesso a um único diretório: sem resultados ...
Marc Gravell

1
Se você tiver problemas quando alguns arquivos não estiverem acessíveis, consulte Enumerando arquivos lançando exceção
CodesInChaos

Respostas:


186

Este artigo cobre tudo que você precisa. Exceto ao contrário de pesquisar os arquivos e comparar nomes, basta imprimir os nomes.

Pode ser modificado da seguinte forma:

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

Adicionado por barlop

GONeale menciona que o acima não lista os arquivos no diretório atual e sugere colocar a parte da lista de arquivos fora da parte que obtém os diretórios. O seguinte faria isso. Ele também inclui uma linha do Writeline que você pode descomentar, que ajuda a rastrear onde você está na recursão que pode ajudar a mostrar as chamadas para ajudar a mostrar como a recursão funciona.

            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
Este método não lista os arquivos para o diretório inicial, apenas seus subdiretórios e inferiores. Gostaria de passar GetFiles GetDirectories fora
GONeale

1
Às vezes, não se deseja os arquivos para o diretório inicial; nesse caso, isso é perfeito para estruturas razoavelmente pequenas. Para listas muito grandes, use algo como solução de Marc Gravell: stackoverflow.com/a/929418/91189
Joseph Gabriel

2
@GONeale está correto. É muito menos plausível para um usuário não esperar a lista de arquivos do diretório raiz de entrada. A palavra entrada é a chave aqui. Foi introduzido por um motivo.
Florin Mircea 07/07

2
Eu tive que adicionar um try catch em torno do laço foreach interna caso contrário ele não continua de erros de acesso negado
Shaun Vermaak

3
Você deve evitar capturar Exception - você realmente deseja capturar uma OutOfMemoryException, por exemplo? Pegue apenas o que você pode manusear.
precisa

435

Observe que no .NET 4.0 há (supostamente) funções de arquivo baseadas em iterador (em vez de baseadas em matriz) incorporadas:

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

No momento eu usaria algo como abaixo; o método recursivo embutido quebra com muita facilidade se você não tiver acesso a um único subdiretório ...; o Queue<string>uso evita muita recursão da pilha de chamadas e o bloco iterador evita que tenhamos uma matriz 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 Em EnumerateFiles ponto reparce recursiva lança IOException "O nome do arquivo não pode ser resolvido pelo sistema"
Serg

5
Para todos os que desejam saber se *.*também inclui arquivos sem extensão de arquivo: Sim, testou um minuto atrás.
Tobias Knauss

1
Para usar isso, você precisará adicionarusing System.IO;
Reinstate Monica - Goodbye SE

7
O @Wikis e para usar Consolevocê precisará adicionar using System;- mas como o IDE pode adicionar todas as usingdiretivas necessárias para você (ctrl +.), E como não estamos usando nada exótico aqui, é comum não incluí-las. Caramba, você também precisará de uma classdefinição, etc. Apenas dizendo
Marc Gravell

1
@MarcGravell Estamos no núcleo .net e mundial Visual Código Estúdio agora, para incluindo o uso de declarações é sempre bem-vindos em qualquer exemplo de código .NET para salvar uma série de pesquisas e inútil "barbear yak"
JohnC

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

2
Como evitar o erro se o usuário de login não tiver acesso em algumas das pastas.
Romil Kumar Jain

5
@ Romil Não acredito que esse trecho de código esteja tentando indicar a funcionalidade completa, apenas a funcionalidade bruta que o OP estava procurando. Obrigado por compartilhar, Pescuma!
kayleeFrye_onDeck

@kayleeFrye_onDeck, coloquei apenas uma preocupação, caso haja um aumento para qualquer uma das pastas ao obter arquivos. Devido a essa preocupação, implementamos nossa função recursiva personalizada.
Romil Kumar Jain 11/03/2015

3
Você receberá "UnauthorizedAccessException" com esta solução. Você deve ter uma solução que possa lidar com erros como este.
Kairan

13

No .NET 4.5, pelo menos, existe esta versão muito mais curta e com o bônus adicional de avaliar qualquer critério de arquivo para inclusão na 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;
    }
}

Use assim:

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

Isso não trata de um caso em que você tem um diretório vazio ... não há nenhuma declaração de retorno dentro da função.
FrumkinWY

@FrumkinWY o que acontece com um diretório vazio? Não tenho uma máquina à mão para testar isso agora.
John Kaster

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

3

No Framework 2.0, você pode usar (lista os arquivos da pasta raiz, é a melhor resposta mais 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);
    }
}

3

Algumas respostas excelentes, mas essas respostas não resolveram meu problema.

Assim que surgir um problema de permissão de pasta: "Permissão negada", o código falha. Isto é o que eu costumava contornar o problema "Permissão negada":

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 isso ajude os outros.


3

Uma solução simples e limpa

/// <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
Você está fazendo manualmente o que DirectoryInfo.GetFiles () fará por você imediatamente - use a sobrecarga com SearchOption.AllDirectories e ele será remunerado por conta própria. Então essa é uma solução complicada .
PhilW

2

Prefiro usar DirectoryInfo porque posso obter FileInfo, não apenas strings.

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

        string searchPattern = "*.xml";

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

Eu faço isso caso, no futuro, precise de filtragem futura ... com base nas propriedades do 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();

Também posso recorrer a strings, se necessário. (e ainda estou revisado para o futuro quanto a filtros / itens da 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();

Observe que " . " É um padrão de pesquisa válido se você deseja arquivar por extensão.


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
Por que o parâmetro filesé ref? Não há necessidade.
Massimiliano Kraus

@MassimilianoKraus Eu diria que, embora não seja obrigatório, fica mais claro que o método dele será alterado filese você não pode mais fornecer apenas um new List<FileInfo>()parâmetro que seria inútil. Pode permitir alguma sub-otimização e evitar a criação de um novo objeto, a menos que seja necessário.
jeromej 7/04

@ JeromeJ, se você souber o que é OOP, sabe que sempre que passa um objeto para um método, esse método pode alterar as propriedades / campos do objeto. Portanto ref, não torna nada mais claro. O refobjetivo é alterar o filesponteiro inteiro, mesmo para o chamador do método: é uma operação perigosa e aqui não há necessidade disso: basta preencher a lista, não é necessário redirecioná-lo para outra lista em a pilha. refdeve ser usado apenas em casos muito particulares; na maioria das vezes, você só precisa implementar as coisas de uma maneira mais paradigm funcional.
Massimiliano Kraus

1

Para evitar isso UnauthorizedAccessException, eu 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 você só precisa de nomes de arquivos e como eu realmente não gostei da maioria das soluções aqui (em termos de recursos ou de legibilidade), que tal essa preguiçosa?

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

Registro mais curto

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

0

Aqui está o meu ponto de vista, com base no de Hernaldo, se você precisar encontrar arquivos com nomes de um determinado padrão, como arquivos XML que em algum lugar do nome contêm uma sequência específica:

// 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

Listando arquivos e pastas para modelar, implementação personalizada.
Isso cria uma lista completa de todos os arquivos e pastas a partir do diretório inicial.

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

0

Solução curta e simples

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

Este me ajudou a obter todos os arquivos em um diretório e subdiretórios, pode ser útil para alguém. [Inspirado nas respostas acima]

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

Alguma versão aprimorada com max lvl para descer no diretório e opção para excluir pastas:

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

diretório de entrada:

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

saída da função (o conteúdo da pasta5 é excluído devido ao limite de lvl e o conteúdo da pasta3 é excluído porque está no array excluídosFolders):

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

-1

Aqui está uma versão do código de B. Clay Shannon não estática para arquivos do 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

Uma solução muito simples, retorna uma lista de arquivos.

    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 ... isso não é recursivo
mxmissile
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.