Поиск файла в каталогах рекурсивно
У меня есть следующий код для рекурсивного поиска файлов через каталог, который возвращает мне список всех xml файлов. Все работает хорошо, за исключением того, что xml файлы в корневом каталоге не включены в список.
Я понимаю, почему, поскольку первое, что он делает, это получить каталоги в корне, а затем получить файлы, тем самым пропуская вызов GetFiles() в корне. Я попытался включить вызов GetFiles() до начала foreach, но результаты не так, как я ожидаю.
public static ArrayList DirSearch(string sDir)
{
try
{
foreach (string d in Directory.GetDirectories(sDir))
{
foreach (string f in Directory.GetFiles(d, "*.xml"))
{
string extension = Path.GetExtension(f);
if (extension != null && (extension.Equals(".xml")))
{
fileList.Add(f);
}
}
DirSearch(d);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return fileList;
}
Моя структура каталогов выглядит примерно так:
RootDirectory
test1.0.xml
test1.1.xml
test1.2.xml
2ndLevDir
test2.0.xml
test2.1.xml
3rdLevDir
test3.0.xml
test3.1.xml
Возврат кода:
test2.0.xml
test2.1.xml
test3.0.xml
test3.1.xml
Я хотел бы вернуть каждый файл, включая:
test1.0.xml
test1.1.xml
test1.2.xml
Не очень хороший стих с рекурсией. Любые указатели будут очень благодарны.
Ответы
Ответ 1
Вы можете использовать эту перегрузку Directory.GetFiles, которая ищет для вас подкаталоги, например:
string[] files = Directory.GetFiles(sDir, "*.xml", SearchOption.AllDirectories);
Так можно искать только одно расширение, но вы можете использовать что-то вроде:
var extensions = new List<string> { ".txt", ".xml" };
string[] files = Directory.GetFiles(sDir, "*.*", SearchOption.AllDirectories)
.Where(f => extensions.IndexOf(Path.GetExtension(f)) >= 0).ToArray();
выбрать файлы с необходимыми расширениями (обратите внимание, что для расширения учитывается регистр).
В некоторых случаях может быть желательно перечислить файлы с помощью метода Directory.EnumerateFiles:
foreach(string f in Directory.EnumerateFiles(sDir, "*.xml", SearchOption.AllDirectories))
{
// do something
}
Консультируйтесь с документацией для исключений, которые могут быть выброшены, такие как UnauthorizedAccessException, если код выполняется под учетной записью, у которой нет соответствующих прав доступа.
Ответ 2
Возвращает все xml файлы рекурсивно:
var allFiles = Directory.GetFiles(path, "*.xml", SearchOption.AllDirectories);
Ответ 3
У вас должен быть цикл над файлами до или после цикла над каталогами, но не вложен в него, как вы это делали.
foreach (string f in Directory.GetFiles(d, "*.xml"))
{
string extension = Path.GetExtension(f);
if (extension != null && (extension.Equals(".xml")))
{
fileList.Add(f);
}
}
foreach (string d in Directory.GetDirectories(sDir))
{
DirSearch(d);
}
Ответ 4
Попробуйте выполнить следующий метод:
public static IEnumerable<string> GetXMLFiles(string directory)
{
List<string> files = new List<string>();
try
{
files.AddRange(Directory.GetFiles(directory, "*.xml", SearchOption.AllDirectories));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return files;
}
Ответ 5
Вы создаете три списка вместо одного (вы не используете возвращаемое значение DirSearch(d)
). Вы можете использовать список в качестве параметра для сохранения состояния:
static void Main(string[] args)
{
var list = new List<string>();
DirSearch(list, ".");
foreach (var file in list)
{
Console.WriteLine(file);
}
}
public static void DirSearch(List<string> files, string startDirectory)
{
try
{
foreach (string file in Directory.GetFiles(startDirectory, "*.*"))
{
string extension = Path.GetExtension(file);
if (extension != null)
{
files.Add(file);
}
}
foreach (string directory in Directory.GetDirectories(startDirectory))
{
DirSearch(files, directory);
}
}
catch (System.Exception e)
{
Console.WriteLine(e.Message);
}
}
Ответ 6
вы можете сделать что-то вроде этого:
foreach (var file in Directory.GetFiles(MyFolder, "*.xml", SearchOption.AllDirectories))
{
// do something with this file
}
Ответ 7
Вам нужно переместить цикл для файлов вне цикла для папок. Кроме того, вам нужно будет передать структуру данных, хранящую коллекцию файлов, каждому вызову метода. Таким образом, все файлы входят в один список.
public static List<string> DirSearch(string sDir, List<string> files)
{
foreach (string f in Directory.GetFiles(sDir, "*.xml"))
{
string extension = Path.GetExtension(f);
if (extension != null && (extension.Equals(".xml")))
{
files.Add(f);
}
}
foreach (string d in Directory.GetDirectories(sDir))
{
DirSearch(d, files);
}
return files;
}
Затем назовите его следующим образом.
List<string> files = DirSearch("c:\foo", new List<string>());
Update:
Хорошо, без ведома, пока я не прочитал другой ответ, для этого уже есть встроенный механизм. Я оставлю свой ответ, если вы заинтересованы в том, как ваш код необходимо изменить, чтобы он работал.
Ответ 8
Использование EnumerateFiles для получения файлов в вложенных каталогах.
Используйте AllDirectories для повторной обработки каталогов через.
using System;
using System.IO;
class Program
{
static void Main()
{
// Call EnumerateFiles in a foreach-loop.
foreach (string file in Directory.EnumerateFiles(@"c:\files",
"*.xml",
SearchOption.AllDirectories))
{
// Display file path.
Console.WriteLine(file);
}
}
}
Ответ 9
Для поиска файлов и каталогов я хотел бы предложить использовать специализированную многопоточную библиотеку .NET, которая обладает широкими возможностями поиска и работает очень быстро.
Всю информацию о библиотеке вы можете найти на GitHub: https://github.com/VladPVS/FastSearchLibrary
Если вы хотите скачать его, вы можете сделать это здесь: https://github.com/VladPVS/FastSearchLibrary/releases
Если у вас есть какие-либо вопросы, пожалуйста, задавайте их.
Это один пример того, как вы можете его использовать:
class Searcher
{
private static object locker = new object();
private FileSearcher searcher;
List<FileInfo> files;
public Searcher()
{
files = new List<FileInfo>(); // create list that will contain search result
}
public void Startsearch()
{
CancellationTokenSource tokenSource = new CancellationTokenSource();
// create tokenSource to get stop search process possibility
searcher = new FileSearcher(@"C:\", (f) =>
{
return Regex.IsMatch(f.Name, @".*[Dd]ragon.*.jpg$");
}, tokenSource); // give tokenSource in constructor
searcher.FilesFound += (sender, arg) => // subscribe on FilesFound event
{
lock (locker) // using a lock is obligatorily
{
arg.Files.ForEach((f) =>
{
files.Add(f); // add the next received file to the search results list
Console.WriteLine($"File location: {f.FullName}, \nCreation.Time: {f.CreationTime}");
});
if (files.Count >= 10) // one can choose any stopping condition
searcher.StopSearch();
}
};
searcher.SearchCompleted += (sender, arg) => // subscribe on SearchCompleted event
{
if (arg.IsCanceled) // check whether StopSearch() called
Console.WriteLine("Search stopped.");
else
Console.WriteLine("Search completed.");
Console.WriteLine($"Quantity of files: {files.Count}"); // show amount of finding files
};
searcher.StartSearchAsync();
// start search process as an asynchronous operation that doesn't block the called thread
}
}