Como listar recursivamente todos os arquivos em um diretório e diretórios filho em C #?
Como listar recursivamente todos os arquivos em um diretório e diretórios filho em C #?
Respostas:
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);
}
}
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];
}
}
}
}
*.*
também inclui arquivos sem extensão de arquivo: Sim, testou um minuto atrás.
using System.IO;
Console
você precisará adicionar using System;
- mas como o IDE pode adicionar todas as using
diretivas 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 class
definição, etc. Apenas dizendo
Directory.GetFiles("C:\\", "*.*", SearchOption.AllDirectories)
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();
IEnumerable<string> GetFilesFromDir(string dir) =>
Directory.EnumerateFiles(dir).Concat(
Directory.EnumerateDirectories(dir)
.SelectMany(subdir => GetFilesFromDir(subdir)));
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);
}
}
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.
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);
}
}
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.
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)
{
}
}
files
é ref
? Não há necessidade.
files
e 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.
ref
, não torna nada mais claro. O ref
objetivo é alterar o files
ponteiro 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. ref
deve ser usado apenas em casos muito particulares; na maioria das vezes, você só precisa implementar as coisas de uma maneira mais paradigm funcional.
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;
}
}
}
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;
}
}
}
Registro mais curto
string files = Directory.GetFiles(@"your_path", "*.jpg", SearchOption.AllDirectories);
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;
}
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);
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);
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);
}
}
}
var d = new DirectoryInfo(@"C:\logs");
var list = d.GetFiles("*.txt").Select(m => m.Name).ToList();
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
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;
}
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;
}
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();
}