递归地在目录中搜索文件

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

对递归不太熟悉,如能提供一些建议,我将不胜感激。

193006 次浏览

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

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

你可以使用 目录的重载为你搜索子目录,例如:

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

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


在某些情况下,最好使用 目录。 EnumerateFiles 方法对文件进行枚举:

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

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

如果 UnauthorizedAccessException 是一个问题,那么请在 Directory. EnumerateFiles = > UnauthorizedAccessException看到正确的答案。

尝试以下方法:

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

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

var allFiles = Directory.GetFiles(path, "*.xml", SearchOption.AllDirectories);

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

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

更新:

我不知道,直到我读到另一个答案,已经有一个内在的机制来做这件事。我会留下我的答案,如果你有兴趣看看你的代码需要如何修改,使其工作。

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

你可以这样做:

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

使用 EnumerateFiles 获取嵌套目录中的文件。 使用 AllDirectory 遍历目录。

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

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

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