Поиск файла в каталогах рекурсивно

У меня есть следующий код для рекурсивного поиска файлов через каталог, который возвращает мне список всех 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, если код выполняется под учетной записью, у которой нет соответствующих прав доступа.

Ответ 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
    }
}