C# 递归搜索目录中的文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9830069/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 10:48:47  来源:igfitidea点击:

Searching for file in directories recursively

c#recursion

提问by Paul

I have the following code to recursively search for files through a directory, which returns a list of all xml files to me. All works well, except that xml files in the root directory are not included in the list.

我有以下代码通过目录递归搜索文件,该目录将所有 xml 文件的列表返回给我。一切正常,除了根目录中的 xml 文件未包含在列表中。

I understand why, since the first thing it does is get the directories in the root, then get files, thus missing the GetFiles() call on the root. I tried including the GetFiles() call prior to the foreach, but the results are not as I expect.

我明白为什么,因为它做的第一件事是获取根目录中的目录,然后获取文件,因此缺少根目录上的 GetFiles() 调用。我尝试在 foreach 之前包含 GetFiles() 调用,但结果与我预期的不同。

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

My directory structure is something like:

我的目录结构类似于:

RootDirectory
        test1.0.xml
            test1.1.xml
            test1.2.xml
  2ndLevDir
            test2.0.xml
            test2.1.xml
  3rdLevDir
               test3.0.xml
               test3.1.xml

Code returns:

代码返回:

test2.0.xml
test2.1.xml
test3.0.xml
test3.1.xml

I would like to return every file including:

我想返回每个文件,包括:

test1.0.xml
test1.1.xml
test1.2.xml

Not very well verse with recursion. Any pointers would be greatly appreciated.

不是很好的递归诗句。任何指针将不胜感激。

采纳答案by Andrew Morton

You could use this overload of Directory.GetFileswhich searches subdirectories for you, for example:

您可以使用Directory.GetFiles 的重载来为您搜索子目录,例如:

string[] files = Directory.GetFiles(sDir, "*.xml", SearchOption.AllDirectories);

Only one extension can be searched for like that, but you could use something like:

只能搜索一个扩展名,但您可以使用以下内容:

var extensions = new List<string> { ".txt", ".xml" };
string[] files = Directory.GetFiles(sDir, "*.*", SearchOption.AllDirectories)
                    .Where(f => extensions.IndexOf(Path.GetExtension(f)) >= 0).ToArray();

to select files with the required extensions (N.B. that is case-sensitive for the extension).

选择具有所需扩展名的文件(注意扩展名区分大小写)。



In some cases it can be desirable to enumerate over the files with the Directory.EnumerateFiles Method:

在某些情况下,可能需要使用Directory.EnumerateFiles 方法枚举文件:

foreach(string f in Directory.EnumerateFiles(sDir, "*.xml", SearchOption.AllDirectories))
{
    // do something
}

Consult the documentation for exceptions which can be thrown, such as UnauthorizedAccessException if the code is running under an account which does not have appropriate access permissions.

如果代码在没有适当访问权限的帐户下运行,请查阅文档以了解可能引发的异常,例如 UnauthorizedAccessException。

回答by Mark Byers

You should have the loop over the files either before or after the loop over the directories, but not nested inside it as you have done.

您应该在目录循环之前或之后对文件进行循环,但不要像您所做的那样嵌套在其中。

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

回答by Bernard

Try the following method:

尝试以下方法:

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

回答by Tim Schmelter

回答by Brian Gideon

You will want to move the loop for the files outside of the loop for the folders. In addition you will need to pass the data structure holding the collection of files to each call of the method. That way all files go into a single list.

您需要将文件的循环移动到文件夹的循环之外。此外,您需要将保存文件集合的数据结构传递给方法的每次调用。这样所有文件都会进入一个列表。

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

Then call it like this.

那就这样称呼吧。

List<string> files = DirSearch("c:\foo", new List<string>());

Update:

更新:

Well unbeknownst to me, until I read the other answer anyway, there is already a builtin mechanism for doing this. I will leave my answer in case you are interested in seeing how your code needs to be modified to make it work.

我不知道,直到我阅读了另一个答案,已经有一个内置的机制可以做到这一点。如果您有兴趣了解需要如何修改代码以使其工作,我会留下我的答案。

回答by Gene

You are creating three lists, instead of using one (you don't use the return value of DirSearch(d)). You can use a list as a parameter to save the state:

您正在创建三个列表,而不是使用一个列表(您不使用 的返回值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);
  }
}

回答by damir

you can do something like this:

你可以这样做:

foreach (var file in Directory.GetFiles(MyFolder, "*.xml", SearchOption.AllDirectories))
        {
            // do something with this file
        }

回答by mercedes

Using EnumerateFiles to get files in nested directories. Use AllDirectories to recurse throught directories.

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

回答by VladVS

For file and directory search purpose I would want to offer use specialized multithreading .NET library that possess a wide search opportunities and works very fast.

出于文件和目录搜索的目的,我想提供使用专门的多线程 .NET 库,这些库拥有广泛的搜索机会并且工作速度非常快。

All information about library you can find on GitHub: https://github.com/VladPVS/FastSearchLibrary

您可以在 GitHub 上找到有关图书馆的所有信息:https: //github.com/VladPVS/FastSearchLibrary

If you want to download it you can do it here: https://github.com/VladPVS/FastSearchLibrary/releases

如果你想下载它,你可以在这里下载:https: //github.com/VladPVS/FastSearchLibrary/releases

If you have any questions please ask them.

如果您有任何问题,请询问他们。

It is one demonstrative example how you can use it:

这是一个如何使用它的示范示例:

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

回答by sspaniel

I tried some of the other solutions listed here, but during unit testing the code would throw exceptions I wanted to ignore. I ended up creating the following recursive search method that will ignore certain exceptions like PathTooLongException and UnauthorizedAccessException.

我尝试了此处列出的其他一些解决方案,但在单元测试期间,代码会抛出我想忽略的异常。我最终创建了以下递归搜索方法,该方法将忽略某些异常,例如 PathTooLongException 和 UnauthorizedAccessException。

    private IEnumerable<string> RecursiveFileSearch(string path, string pattern, ICollection<string> filePathCollector = null)
    {
        try
        {
            filePathCollector = filePathCollector ?? new LinkedList<string>();

            var matchingFilePaths = Directory.GetFiles(path, pattern);

            foreach(var matchingFile in matchingFilePaths)
            {
                filePathCollector.Add(matchingFile);
            }

            var subDirectories = Directory.EnumerateDirectories(path);

            foreach (var subDirectory in subDirectories)
            {
                RecursiveFileSearch(subDirectory, pattern, filePathCollector);
            }

            return filePathCollector;
        }
        catch (Exception error)
        {
            bool isIgnorableError = error is PathTooLongException ||
                error is UnauthorizedAccessException;

            if (isIgnorableError)
            {
                return Enumerable.Empty<string>();
            }

            throw error;
        }
    }