Reputation: 1817
I have the following code:
List<string> result = new List<string>();
foreach (string file in Directory.EnumerateFiles(path,"*.*",
SearchOption.AllDirectories)
.Where(s => s.EndsWith(".mp3") || s.EndsWith(".wma")))
{
result.Add(file);
}
It works fine and does what I need. Except for one small thing. I would like to find a better way to filter on multiple extensions. I would like to use a string array with filters such as this:
string[] extensions = { "*.mp3", "*.wma", "*.mp4", "*.wav" };
What is the most efficient way to do this using NET Framework 4.0/LINQ? Any suggestions?
I'd appreciate any help being an occasional programmer :-)
Upvotes: 95
Views: 108777
Reputation: 3833
This is the simplest solution I could think of:
var tracks = di.EnumerateFiles("*.mp3").ToList();
tracks.AddRange(di.EnumerateFiles("*.wma"));
Upvotes: 0
Reputation: 449
Yevhan's answer is by far the cleanest and most modern, but I would take it one step further and create a FileSystemEnumerable<FileInfo>
directly. This will give you the added benefit of being able to provide your own transform (to in this case get a FileInfo
object rather than a file path), and specify a ShouldIncludePredicate
predicate to execute all your desired filters at once, with out needing multiple enumerations.
var path = @"C:\SEARCH\DIRECTORY";
IReadOnlyList<string> filters = [
"*.xlsx",
"*.xls"
];
bool FilterPrediate(ref FileSystemEntry f)
{
if (f.IsDirectory)
return false;
foreach (var filter in filters)
if (FileSystemName.MatchesSimpleExpression(filter, f.FileName))
return true;
return false;
}
var enumerable = new FileSystemEnumerable<FileInfo>(path, (ref FileSystemEntry f) => (FileInfo)f.ToFileSystemInfo())
{
ShouldIncludePredicate = FilterPrediate
};
foreach (FileInfo file in enumerable)
{
// TODO do work
}
Upvotes: 0
Reputation: 483
To filter using a Regex pattern and file Modified date.
In the example it return files that:
Bck
for extensions tgz
and xml
200
days.Returns Full path name
string startPath = @"c:\temp";
int olderThanDays = 200;
Regex re = new Regex($@"^Bck.*\.tgz|^Bck.*\.xml");
var files = new DirectoryInfo(startPath)
.EnumerateFiles($"*.*", SearchOption.AllDirectories)
.Where(f => f.CreationTime < DateTime.Now.AddDays(-olderThanDays) && re.IsMatch(f.Name))
.Select(f => f.FullName).ToList();
Upvotes: 0
Reputation: 851
Beginning from the NET Core 2.1 and .NET Standard 2.1 there is built-in class FileSystemName: documentation, source code which provides methods for matching file system names:
Example:
public static IEnumerable<string> EnumerateFiles(string path, string[] searchPatterns, SearchOption searchOption = SearchOption.TopDirectoryOnly)
{
return Directory.EnumerateFiles(path, "*", searchOption)
.Where(fileName => searchPatterns.Any(pattern => FileSystemName.MatchesSimpleExpression(pattern, fileName)));
}
I've adapted the existing source code of FileSystemName to be used in .NetFramework 4: Gist FileSystemName for .NetFramework 4.
Upvotes: 1
Reputation: 489
I solved this problem this way:
string[] formats = {".mp3", ".wma", ".mp4"};
foreach (var file in Directory.EnumerateFiles(folder, "*.*", SearchOption.AllDirectories).Where(x => formats.Any(x.EndsWith)))
{
// TODO...
}
Upvotes: 7
Reputation: 31
For Filtering using the same File Extensions list strings as GUI Open Dialogs e.g.:
".exe,.pdb".Split(',', ';', '|').SelectMany(_ => Directory.EnumerateFiles(".", "*" + _, searchOptions)
Packaged up:
public static IEnumerable<string> EnumerateFilesFilter(string path, string filesFilter, SearchOption searchOption = SearchOption.TopDirectoryOnly)
{
return filesFilter.Split(',', ';', '|').SelectMany(_ => Directory.EnumerateFiles(path, "*" + _, searchOption));
}
Upvotes: 1
Reputation: 10167
The most elegant approach is probably:
var directory = new DirectoryInfo(path);
var masks = new[] { "*.mp3", "*.wav" };
var files = masks.SelectMany(directory.EnumerateFiles);
But it might not be the most efficient.
Upvotes: 37
Reputation: 39695
I created some helper methods to solve this which I blogged about earlier this year.
One version takes a regex pattern \.mp3|\.mp4
, and the other a string list and runs in parallel.
public static class MyDirectory
{ // Regex version
public static IEnumerable<string> GetFiles(string path,
string searchPatternExpression = "",
SearchOption searchOption = SearchOption.TopDirectoryOnly)
{
Regex reSearchPattern = new Regex(searchPatternExpression, RegexOptions.IgnoreCase);
return Directory.EnumerateFiles(path, "*", searchOption)
.Where(file =>
reSearchPattern.IsMatch(Path.GetExtension(file)));
}
// Takes same patterns, and executes in parallel
public static IEnumerable<string> GetFiles(string path,
string[] searchPatterns,
SearchOption searchOption = SearchOption.TopDirectoryOnly)
{
return searchPatterns.AsParallel()
.SelectMany(searchPattern =>
Directory.EnumerateFiles(path, searchPattern, searchOption));
}
}
Upvotes: 96
Reputation: 1264
As I noted in a comment, while Mikael Svenson's helper methods are great little solutions, if you're ever trying to do something for a one-off project in a hurry again, consider the Linq extension .Union( ). This allows you to join together two enumerable sequences. In your case, the code would look like this:
List<string> result = Directory.EnumerateFiles(path,"*.mp3", SearchOption.AllDirectories)
.Union(Directory.EnumerateFiles(path, ".wma", SearchOption.AllDirectories)).ToList();
This creates and fills your result list all in one line.
Upvotes: 16
Reputation: 81
I know this is an old post but I came up with a solution people might like to use.
private IEnumerable<FileInfo> FindFiles()
{
DirectoryInfo sourceDirectory = new DirectoryInfo(@"C:\temp\mydirectory");
string foldersFilter = "*bin*,*obj*";
string fileTypesFilter = "*.mp3,*.wma,*.mp4,*.wav";
// filter by folder name and extension
IEnumerable<DirectoryInfo> directories = foldersFilter.Split(',').SelectMany(pattern => sourceDirectory.EnumerateDirectories(pattern, SearchOption.AllDirectories));
List<FileInfo> files = new List<FileInfo>();
files.AddRange(directories.SelectMany(dir => fileTypesFilter.Split(',').SelectMany(pattern => dir.EnumerateFiles(pattern, SearchOption.AllDirectories))));
// Pick up root files
files.AddRange(fileTypesFilter.Split(',').SelectMany(pattern => sourceDirectory.EnumerateFiles(fileTypesFilter, SearchOption.TopDirectoryOnly)));
// filter just by extension
IEnumerable<FileInfo> files2 = fileTypesFilter.Split(',').SelectMany(pattern => sourceDirectory.EnumerateFiles(pattern, SearchOption.AllDirectories));
}
Upvotes: 2
Reputation: 1469
string path = "C:\\";
var result = new List<string>();
string[] extensions = { ".mp3", ".wma", ".mp4", ".wav" };
foreach (string file in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories)
.Where(s => extensions.Any(ext => ext == Path.GetExtension(s))))
{
result.Add(file);
Console.WriteLine(file);
}
Upvotes: 23