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