递归搜索目录中的文件


71

我有以下代码通过目录递归搜索文件,该目录向我返回了所有xml文件的列表。除根目录中的xml文件未包含在列表中之外,其他所有方法都运行良好。

我知道为什么,因为这样做的第一件事是获取根目录中的目录,然后获取文件,从而在根目录上缺少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;
}

我的目录结构是这样的:

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

没有很好的诗歌与递归。任何指针将不胜感激。


4
Directory.EnumerateFiles(sDir, "*.xml", SearchOption.AllDirectories)
LB

Answers:


144

您可以使用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();

选择带有所需扩展名的文件(扩展名区分大小写的NB)。


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

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

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



3

请尝试以下方法:

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

3

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

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

2

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

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

更新:

对我来说不为人所知,在我仍然阅读其他答案之前,已经有一个内置的机制可以执行此操作。如果您有兴趣查看如何修改代码以使其正常工作,我将留下我的答案。


2

您可以执行以下操作:

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

2

我尝试了此处列出的其他一些解决方案,但是在单元测试期间,代码将引发我想忽略的异常。我最终创建了以下递归搜索方法,该方法将忽略某些异常,例如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;
        }
    }

1

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

0

出于文件和目录搜索的目的,我想提供使用专用的多线程.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
    }
}

哎呀,这看起来是一个非常昂贵的搜索,尤其是对于可以通过常规框架可能性实现的搜索(并且正则表达式过滤可以用作枚举文件结果的where语句),更不用说消费者必须实施锁:(
Icepickle

当然,您可以使用简单的搜索。这样的事情:List <FileInfo> files = FileSearcher.GetFilesFast(@“ C:\ Users”,“ SomePattern .txt”); 而且,当您找到系统文件时,“正常的框架可能性”将引发UnauthorizedAccessException,但此库将阻止它。
VladVS

@Icepickle,建议您将此库的方法与解决此问题的代码进行比较。尤其是工作速度。
VladVS
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.