递归搜索目录中的文件

Posted

技术标签:

【中文标题】递归搜索目录中的文件【英文标题】:Searching for file in directories recursively 【发布时间】:2012-04-07 11:22:40 【问题描述】:

我有以下代码通过目录递归搜索文件,该目录将所有 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

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

【问题讨论】:

Directory.EnumerateFiles(sDir, "*.xml", SearchOption.AllDirectories) Recursively search directories in C#的可能重复 【参考方案1】:

您可以使用this overload of 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 Method 枚举文件:

foreach(string f in Directory.EnumerateFiles(sDir, "*.xml", SearchOption.AllDirectories))

    // do something

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

如果 UnauthorizedAccessException 有问题,请在Directory.EnumerateFiles => UnauthorizedAccessException 上查看详细答案。

【讨论】:

【参考方案2】:

这会递归返回所有 xml 文件:

var allFiles = Directory.GetFiles(path, "*.xml", SearchOption.AllDirectories);
http://msdn.microsoft.com/en-us/library/ms143316%28v=vs.100%29.aspx http://msdn.microsoft.com/en-us/library/ms143448.aspx#Y252

【讨论】:

【参考方案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;

【讨论】:

【参考方案4】:

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

【讨论】:

【参考方案5】:

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

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

更新:

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

【讨论】:

【参考方案6】:

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

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

【讨论】:

【参考方案7】:

你可以这样做:

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

【讨论】:

【参考方案8】:

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

【讨论】:

【参考方案9】:

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

【讨论】:

【参考方案10】:

出于文件和目录搜索的目的,我想提供使用专门的多线程 .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 语句),更不用说消费者必须实现锁:( 当然可以使用简单搜索。像这样: List files = FileSearcher.GetFilesFast(@"C:\Users", "SomePattern.txt");此外,当你找到系统文件时,“正常的框架可能性”会抛出 UnauthorizedAccessException,但这个库会抑制它。 @Icepickle,我建议您将此库的方法与解决此问题的代码进行比较。尤其是工作速度。

以上是关于递归搜索目录中的文件的主要内容,如果未能解决你的问题,请参考以下文章

使用 PHP 递归搜索目录中的文件并更改值

递归搜索根目录和子文件夹中的文件[重复]

使用 find 以非递归方式搜索目录中的特定文件。艾克斯

如何让 pip 递归搜索目录中的包和依赖项?

MEF递归插件搜索

Node.js fs.readdir 递归目录搜索