我正在用C#和.net 4.0在Visual Studio 2012中构建一个Windows应用程序。
该程序的功能之一是搜索满足给定搜索条件的所有文件,通常(但并非总是)包含通配符。
搜索条件在运行时是未知的;它是从 Excel 电子表格导入的。
可能的搜索条件可以包括以下内容:
- 确切路径
- "C:\temp\directory1\directory2\someFile.txt"
- 路径,文件名中带有通配符:
- "C:\temp\directory1\directory2*.*"
- 文件名,路径中带有通配符:
- "C:\temp*\directory*\someFile.txt"
- 带通配符的文件名和路径:
- "C:\temp\*\*
- \*.*"
- 以上所有内容,具有任意目录结构:
- "C:\temp\dir*
- 1\dir*\otherdir\*\other*\file*.txt"
- "C:\te*\*\someFile.txt"
- "C:\temp\*tory1\dire*2 \*\*\*\*\*.*"
我尝试使用Directory.EnumerateFiles
:
IEnumerable<string> matchingFilePaths = System.IO.Directory.EnumerateFiles(@"C:", selectedItemPath[0], System.IO.SearchOption.AllDirectories);
但是,这仅适用于上述情况 2。尝试在文件夹名称中将Directory.EnumerateFiles
与通配符一起使用会导致"非法字符"豁免。
我希望 .net 中有一个单行代码,我可以用来进行此文件搜索。通配符的数量和目录结构的深度在运行时是未知的,可以想象搜索可能必须深入一百个文件夹,每个文件夹都包含未知数量的通配符。(这是问题的症结所在)。试图避免嵌套循环的数量过多。
我在这里阅读了解决方案,但这似乎不适用于任意文件夹结构。
既然你已经回答了你自己的问题,我想我会把我的尝试发布给任何可能发现这个并且不想使用 powershell 的人。它都是延迟加载的,因此在您拥有大型文件系统并且匹配大量文件的情况下,其性能将是最佳的。
示例用法:
string pattern = @"C:Users*SourceRepos**.cs";
foreach (var st in GetAllMatchingPaths(pattern))
Console.WriteLine(st);
溶液:
public static IEnumerable<string> GetAllMatchingPaths(string pattern)
{
char separator = Path.DirectorySeparatorChar;
string[] parts = pattern.Split(separator);
if (parts[0].Contains('*') || parts[0].Contains('?'))
throw new ArgumentException("path root must not have a wildcard", nameof(parts));
return GetAllMatchingPathsInternal(String.Join(separator.ToString(), parts.Skip(1)), parts[0]);
}
private static IEnumerable<string> GetAllMatchingPathsInternal(string pattern, string root)
{
char separator = Path.DirectorySeparatorChar;
string[] parts = pattern.Split(separator);
for (int i = 0; i < parts.Length; i++)
{
// if this part of the path is a wildcard that needs expanding
if (parts[i].Contains('*') || parts[i].Contains('?'))
{
// create an absolute path up to the current wildcard and check if it exists
var combined = root + separator + String.Join(separator.ToString(), parts.Take(i));
if (!Directory.Exists(combined))
return new string[0];
if (i == parts.Length - 1) // if this is the end of the path (a file name)
{
return Directory.EnumerateFiles(combined, parts[i], SearchOption.TopDirectoryOnly);
}
else // if this is in the middle of the path (a directory name)
{
var directories = Directory.EnumerateDirectories(combined, parts[i], SearchOption.TopDirectoryOnly);
var paths = directories.SelectMany(dir =>
GetAllMatchingPathsInternal(String.Join(separator.ToString(), parts.Skip(i + 1)), dir));
return paths;
}
}
}
// if pattern ends in an absolute path with no wildcards in the filename
var absolute = root + separator + String.Join(separator.ToString(), parts);
if (File.Exists(absolute))
return new[] { absolute };
return new string[0];
}
PS:它不会匹配目录,只会匹配文件,但如果需要,您可以轻松修改它。
经过更多的搜索,结果发现有一个非常简单的解决方案。我可以使用Windows Powershell Get-ChildItem
命令:
using System.Management.Automation;
PowerShell ps = PowerShell.Create();
ps.AddCommand("Get-ChildItem");
ps.AddArgument(selectedItemPath[0]);
foreach (var result in ps.Invoke())
{
//display the results in a textbox
}
这允许人们避免嵌套的for
循环。