I am trying to use the Directory.GetFiles()
method to retrieve a list of files of multiple types, such as mp3
\'s and jpg
\'s. I have t
I had the same problem and couldn't find the right solution so I wrote a function called GetFiles:
/// <summary>
/// Get all files with a specific extension
/// </summary>
/// <param name="extensionsToCompare">string list of all the extensions</param>
/// <param name="Location">string of the location</param>
/// <returns>array of all the files with the specific extensions</returns>
public string[] GetFiles(List<string> extensionsToCompare, string Location)
{
List<string> files = new List<string>();
foreach (string file in Directory.GetFiles(Location))
{
if (extensionsToCompare.Contains(file.Substring(file.IndexOf('.')+1).ToLower())) files.Add(file);
}
files.Sort();
return files.ToArray();
}
This function will call Directory.Getfiles()
only one time.
For example call the function like this:
string[] images = GetFiles(new List<string>{"jpg", "png", "gif"}, "imageFolder");
EDIT: To get one file with multiple extensions use this one:
/// <summary>
/// Get the file with a specific name and extension
/// </summary>
/// <param name="filename">the name of the file to find</param>
/// <param name="extensionsToCompare">string list of all the extensions</param>
/// <param name="Location">string of the location</param>
/// <returns>file with the requested filename</returns>
public string GetFile( string filename, List<string> extensionsToCompare, string Location)
{
foreach (string file in Directory.GetFiles(Location))
{
if (extensionsToCompare.Contains(file.Substring(file.IndexOf('.') + 1).ToLower()) &&& file.Substring(Location.Length + 1, (file.IndexOf('.') - (Location.Length + 1))).ToLower() == filename)
return file;
}
return "";
}
For example call the function like this:
string image = GetFile("imagename", new List<string>{"jpg", "png", "gif"}, "imageFolder");
If you are using VB.NET (or imported the dependency into your C# project), there actually exists a convenience method that allows to filter for multiple extensions:
Microsoft.VisualBasic.FileIO.FileSystem.GetFiles("C:\\path", Microsoft.VisualBasic.FileIO.SearchOption.SearchAllSubDirectories, new string[] {"*.mp3", "*.jpg"});
In VB.NET this can be accessed through the My-namespace:
My.Computer.FileSystem.GetFiles("C:\path", FileIO.SearchOption.SearchAllSubDirectories, {"*.mp3", "*.jpg"})
Unfortunately, these convenience methods don't support a lazily evaluated variant like Directory.EnumerateFiles()
does.
for
var exts = new[] { "mp3", "jpg" };
You could:
public IEnumerable<string> FilterFiles(string path, params string[] exts) {
return
Directory
.EnumerateFiles(path, "*.*")
.Where(file => exts.Any(x => file.EndsWith(x, StringComparison.OrdinalIgnoreCase)));
}
Directory.EnumerateFiles
for a performance boost (What is the difference between Directory.EnumerateFiles vs Directory.GetFiles?).EndsWith("aspx", StringComparison.OrdinalIgnoreCase)
rather than .ToLower().EndsWith("aspx")
)But the real benefit of EnumerateFiles
shows up when you split up the filters and merge the results:
public IEnumerable<string> FilterFiles(string path, params string[] exts) {
return
exts.Select(x => "*." + x) // turn into globs
.SelectMany(x =>
Directory.EnumerateFiles(path, x)
);
}
It gets a bit faster if you don't have to turn them into globs (i.e. exts = new[] {"*.mp3", "*.jpg"}
already).
Performance evaluation based on the following LinqPad test (note: Perf
just repeats the delegate 10000 times)
https://gist.github.com/zaus/7454021
( reposted and extended from 'duplicate' since that question specifically requested no LINQ: Multiple file-extensions searchPattern for System.IO.Directory.GetFiles )
in .NET 2.0 (no Linq):
public static List<string> GetFilez(string path, System.IO.SearchOption opt, params string[] patterns)
{
List<string> filez = new List<string>();
foreach (string pattern in patterns)
{
filez.AddRange(
System.IO.Directory.GetFiles(path, pattern, opt)
);
}
// filez.Sort(); // Optional
return filez; // Optional: .ToArray()
}
Then use it:
foreach (string fn in GetFilez(path
, System.IO.SearchOption.AllDirectories
, "*.xml", "*.xml.rels", "*.rels"))
{}
There is also a descent solution which seems not to have any memory or performance overhead and be quite elegant:
string[] filters = new[]{"*.jpg", "*.png", "*.gif"};
string[] filePaths = filters.SelectMany(f => Directory.GetFiles(basePath, f)).ToArray();
The following function searches on multiple patterns, separated by commas. You can also specify an exclusion, eg: "!web.config" will search for all files and exclude "web.config". Patterns can be mixed.
private string[] FindFiles(string directory, string filters, SearchOption searchOption)
{
if (!Directory.Exists(directory)) return new string[] { };
var include = (from filter in filters.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries) where !string.IsNullOrEmpty(filter.Trim()) select filter.Trim());
var exclude = (from filter in include where filter.Contains(@"!") select filter);
include = include.Except(exclude);
if (include.Count() == 0) include = new string[] { "*" };
var rxfilters = from filter in exclude select string.Format("^{0}$", filter.Replace("!", "").Replace(".", @"\.").Replace("*", ".*").Replace("?", "."));
Regex regex = new Regex(string.Join("|", rxfilters.ToArray()));
List<Thread> workers = new List<Thread>();
List<string> files = new List<string>();
foreach (string filter in include)
{
Thread worker = new Thread(
new ThreadStart(
delegate
{
string[] allfiles = Directory.GetFiles(directory, filter, searchOption);
if (exclude.Count() > 0)
{
lock (files)
files.AddRange(allfiles.Where(p => !regex.Match(p).Success));
}
else
{
lock (files)
files.AddRange(allfiles);
}
}
));
workers.Add(worker);
worker.Start();
}
foreach (Thread worker in workers)
{
worker.Join();
}
return files.ToArray();
}
Usage:
foreach (string file in FindFiles(@"D:\628.2.11", @"!*.config, !*.js", SearchOption.AllDirectories))
{
Console.WriteLine(file);
}