Add LINQ Where Clause dynamically - c#

I have the following code that gets files and folders from directory:
var files = Directory.EnumerateFiles(sourceDIR.Text, "*.*", SearchOption.AllDirectories).Where(s => s.EndsWith(".mp4") || s.EndsWith(".mov"));
However I have a comboBox with the file extensions I want to loop through, how can I add them dynamically to where clause?

To get a list of the extensions from the combo box, then you can use
.Where(s => ListOfExtensions.Contains(Path.GetExtension(s)));

You could write String extension class
internal static class StringFileExtension
{
public static bool EndsWithFromList(this string fileInfo, List<string> fileExtensions)
{
foreach (var extension in fileExtensions)
{
if (fileInfo.EndsWith(extension))
return true;
}
return false;
}
}
and then just use it like this:
List<string> fileExtensions = new List<string>
{
".mp4",
".mov",
"..."
};
var files = Directory.EnumerateFiles(sourceDIR.Text, "*.*", SearchOption.AllDirectories).Where(s => s.EndsWithFromList(fileExtensions));
don't forget include extensions namespace in usage place

Related

Validate file extensions match approved list

I have a list of list of files that I want to against a list of approved extensions.
The list of approved extensions may be either
Mandatory or Optional
I need to handle two cases
Check that the list of files contains all mandatory extensions
The list of files may only contain extensions in the approved list
Have been trying with regex because some cases can be grouped together. For example .docx and .doc treated as the same
Here is what I have so far (pseudo code)
List<string[]> approvedExt = new List<string[]>();
// M - Mandatory
// O - Optional
approvedExt.Add(new[] { "pdf", "M" });
approvedExt.Add(new[] { "(docx|doc)", "M" }); //Handle as one case
approvedExt.Add(new[] { "(txt)", "O" });
//Example list
List<string> fileList = new List<string>();
fileList.Add("123.pdf");
fileList.Add("123.txt");
fileList.Add("123.xlsx");
fileList.Add("123.pdf");
//pseudo code
For each ext in approvedExt (that are Mandatory)
{
bool checkMandatoryExt = Any file match?
//Example code I have seen
fileList.All(f => System.Text.RegularExpressions.Regex.IsMatch(f, pattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase));
}
if (!checkMandatoryExt)
{
//Handle Error
}
for each file in fileList
{
bool allApprovedExt = Any patterns match?
}
if (!allApprovedExt)
{
//Handle Error
}
The example file list above would fail 2 cases
Contains a .xlsx file (Not in the approved ext list)
Does contain neither a .docx nor .doc file (Mandatory extension not in the file list)
I would like to be able to pass a list of files names and a list of approved extensions and return true/false if the list files passes the two checks above.
Thank you
Here is how I would solve it (pseudo code):
public class Condition
{
public bool Mandatory {get;set;}
public string[] Extensions {get;set;}
}
// ...
//NOTE: includes the . before the extension
public string[] GetExtensions(IEnumerable<string> files)
{
return files.Select(f => Path.GetExtension(f).ToLower()??"").Distinct().ToArray();
}
public bool AllConditionsOk(string[] fileNamesToCheck, Condition[] conditions)
{
//Extract Extension only (e.g. Path.GetExtension)
string[] extensions = GetExtensions(fileNamesToCheck);
//Check if any existing extension is not allowed
foreach(string extension in extensions)
{
if(!conditions.Any(c => c.Extensions.Contains(extension)))
return false;
}
//Check if every mandatory condition is fulfilled
foreach(Condition condition in conditions.Where(c => c.Mandatory))
{
if(!condition.Extensions.Any(e => extensions.Contains(e)))
return false;
}
return true;
}
Or if you prefer a short version:
return extensions.Any(extension => !conditions.Any(c => c.Extensions.Contains(extension))) &&
conditions.Where(c => c.Mandatory)
.All(condition => condition.Extensions.Any(e => extensions.Contains(e)));
If you need to check extensions in list of filenames contains in extension list you can do it like this:
foreach(var file in filelist)
{
approvedExt.Contains(file.split(".").Last();
}

Getting all files matching one of many extensions using System.IO.Directory

I'm trying to get all files from a directory that are one of these extensions: .aif, .wav, .mp3, or .ogg.
Can I somehow combine these in a searchpattern, or do I have to iterate through all files once per extension?
This is what I'd like:
foreach (string filePath in Directory.GetFiles(pathRoot + "/Music/" + path, "*.mp3" || "*.aif" || "*.wav" || "*.ogg"))
{
//Do stuff
}
If you need to check only for extensions then easiest solution I can think of is to get all files and compare extension with list:
var extList = new string[] { ".mp3", ".aif", ".wav", ".ogg" };
var files = Directory.GetFiles(pathRoot + "/Music/" + path, "*.*")
.Where(n => extList.Contains(System.IO.Path.GetExtension(n), StringComparer.OrdinalIgnoreCase))
.ToList();
There is no method to search for multiple patterns but you can write one:
private string[] GetFiles(string path, params string[] searchPatterns)
{
var filePaths = new List<string>();
foreach (var searchPattern in searchPatterns)
{
filePaths.AddRange(Directory.GetFiles(path, searchPattern));
}
filePaths.Sort();
return filePaths.ToArray();
}
You can now call that where you would originally have called Directory.GetFiles and pass multiple patterns.

Multiple file-extensions searchPattern for System.IO.Directory.GetFiles

What is the syntax for setting multiple file-extensions as searchPattern on Directory.GetFiles()? For example filtering out files with .aspx and .ascx extensions.
// TODO: Set the string 'searchPattern' to only get files with
// the extension '.aspx' and '.ascx'.
var filteredFiles = Directory.GetFiles(path, searchPattern);
Update: LINQ is not an option, it has to be a searchPattern passed into GetFiles, as specified in the question.
var filteredFiles = Directory
.GetFiles(path, "*.*")
.Where(file => file.ToLower().EndsWith("aspx") || file.ToLower().EndsWith("ascx"))
.ToList();
Edit 2014-07-23
You can do this in .NET 4.5 for a faster enumeration:
var filteredFiles = Directory
.EnumerateFiles(path) //<--- .NET 4.5
.Where(file => file.ToLower().EndsWith("aspx") || file.ToLower().EndsWith("ascx"))
.ToList();
Directory.EnumerateFiles in MSDN
I like this method, because it is readable and avoids multiple iterations of the directory:
var allowedExtensions = new [] {".doc", ".docx", ".pdf", ".ppt", ".pptx", ".xls", ".xslx"};
var files = Directory
.GetFiles(folder)
.Where(file => allowedExtensions.Any(file.ToLower().EndsWith))
.ToList();
I believe there is no "out of the box" solution, that's a limitation of the Directory.GetFiles method.
It's fairly easy to write your own method though, here is an example.
The code could be:
/// <summary>
/// Returns file names from given folder that comply to given filters
/// </summary>
/// <param name="SourceFolder">Folder with files to retrieve</param>
/// <param name="Filter">Multiple file filters separated by | character</param>
/// <param name="searchOption">File.IO.SearchOption,
/// could be AllDirectories or TopDirectoryOnly</param>
/// <returns>Array of FileInfo objects that presents collection of file names that
/// meet given filter</returns>
public string[] getFiles(string SourceFolder, string Filter,
System.IO.SearchOption searchOption)
{
// ArrayList will hold all file names
ArrayList alFiles = new ArrayList();
// Create an array of filter string
string[] MultipleFilters = Filter.Split('|');
// for each filter find mathing file names
foreach (string FileFilter in MultipleFilters)
{
// add found file names to array list
alFiles.AddRange(Directory.GetFiles(SourceFolder, FileFilter, searchOption));
}
// returns string array of relevant file names
return (string[])alFiles.ToArray(typeof(string));
}
GetFiles can only match a single pattern, but you can use Linq to invoke GetFiles with multiple patterns:
FileInfo[] fi = new string[]{"*.txt","*.doc"}
.SelectMany(i => di.GetFiles(i, SearchOption.AllDirectories))
.ToArray();
See comments section here:
http://www.codeproject.com/KB/aspnet/NET_DirectoryInfo.aspx
var filteredFiles = Directory
.EnumerateFiles(path, "*.*") // .NET4 better than `GetFiles`
.Where(
// ignorecase faster than tolower...
file => file.ToLower().EndsWith("aspx")
|| file.EndsWith("ascx", StringComparison.OrdinalIgnoreCase))
.ToList();
Don't forget the new .NET4 Directory.EnumerateFiles for a performance boost (What is the difference between Directory.EnumerateFiles vs Directory.GetFiles?)
"IgnoreCase" should be faster than "ToLower"
Or, it may be faster to split and merge your globs (at least it looks cleaner):
"*.ext1;*.ext2".Split(';')
.SelectMany(g => Directory.EnumerateFiles(path, g))
.ToList();
I fear you will have to do somthing like this, I mutated the regex from here.
var searchPattern = new Regex(
#"$(?<=\.(aspx|ascx))",
RegexOptions.IgnoreCase);
var files = Directory.EnumerateFiles(path)
.Where(f => searchPattern.IsMatch(f))
.ToList();
The easy-to-remember, lazy and perhaps imperfect solution:
Directory.GetFiles(dir, "*.dll").Union(Directory.GetFiles(dir, "*.exe"))
I would use the following:
var ext = new string[] { ".ASPX", ".ASCX" };
FileInfo[] collection = (from fi in new DirectoryInfo(path).GetFiles()
where ext.Contains(fi.Extension.ToUpper())
select fi)
.ToArray();
EDIT: corrected due mismatch between Directory and DirectoryInfo
I would try to specify something like
var searchPattern = "as?x";
it should work.
A more efficient way of getting files with the extensions ".aspx" and ".ascx"
that avoids querying the file system several times and avoids returning a lot of undesired files, is to pre-filter the files by using an approximate search pattern and to refine the result afterwards:
var filteredFiles = Directory.GetFiles(path, "*.as?x")
.Select(f => f.ToLowerInvariant())
.Where(f => f.EndsWith("px") || f.EndsWith("cx"))
.ToList();
/// <summary>
/// Returns the names of files in a specified directories that match the specified patterns using LINQ
/// </summary>
/// <param name="srcDirs">The directories to seach</param>
/// <param name="searchPatterns">the list of search patterns</param>
/// <param name="searchOption"></param>
/// <returns>The list of files that match the specified pattern</returns>
public static string[] GetFilesUsingLINQ(string[] srcDirs,
string[] searchPatterns,
SearchOption searchOption = SearchOption.AllDirectories)
{
var r = from dir in srcDirs
from searchPattern in searchPatterns
from f in Directory.GetFiles(dir, searchPattern, searchOption)
select f;
return r.ToArray();
}
public static bool CheckFiles(string pathA, string pathB)
{
string[] extantionFormat = new string[] { ".war", ".pkg" };
return CheckFiles(pathA, pathB, extantionFormat);
}
public static bool CheckFiles(string pathA, string pathB, string[] extantionFormat)
{
System.IO.DirectoryInfo dir1 = new System.IO.DirectoryInfo(pathA);
System.IO.DirectoryInfo dir2 = new System.IO.DirectoryInfo(pathB);
// Take a snapshot of the file system. list1/2 will contain only WAR or PKG
// files
// fileInfosA will contain all of files under path directories
FileInfo[] fileInfosA = dir1.GetFiles("*.*",
System.IO.SearchOption.AllDirectories);
// list will contain all of files that have ..extantion[]
// Run on all extantion in extantion array and compare them by lower case to
// the file item extantion ...
List<System.IO.FileInfo> list1 = (from extItem in extantionFormat
from fileItem in fileInfosA
where extItem.ToLower().Equals
(fileItem.Extension.ToLower())
select fileItem).ToList();
// Take a snapshot of the file system. list1/2 will contain only WAR or
// PKG files
// fileInfosA will contain all of files under path directories
FileInfo[] fileInfosB = dir2.GetFiles("*.*",
System.IO.SearchOption.AllDirectories);
// list will contain all of files that have ..extantion[]
// Run on all extantion in extantion array and compare them by lower case to
// the file item extantion ...
List<System.IO.FileInfo> list2 = (from extItem in extantionFormat
from fileItem in fileInfosB
where extItem.ToLower().Equals
(fileItem.Extension.ToLower())
select fileItem).ToList();
FileCompare myFileCompare = new FileCompare();
// This query determines whether the two folders contain
// identical file lists, based on the custom file comparer
// that is defined in the FileCompare class.
return list1.SequenceEqual(list2, myFileCompare);
}
Instead of the EndsWith function, I would choose to use the Path.GetExtension() method instead. Here is the full example:
var filteredFiles = Directory.EnumerateFiles( path )
.Where(
file => Path.GetExtension(file).Equals( ".aspx", StringComparison.OrdinalIgnoreCase ) ||
Path.GetExtension(file).Equals( ".ascx", StringComparison.OrdinalIgnoreCase ) );
or:
var filteredFiles = Directory.EnumerateFiles(path)
.Where(
file => string.Equals( Path.GetExtension(file), ".aspx", StringComparison.OrdinalIgnoreCase ) ||
string.Equals( Path.GetExtension(file), ".ascx", StringComparison.OrdinalIgnoreCase ) );
(Use StringComparison.OrdinalIgnoreCase if you care about performance: MSDN string comparisons)
You can do it like this
new DirectoryInfo(path).GetFiles().Where(Current => Regex.IsMatch(Current.Extension, "\\.(aspx|ascx)", RegexOptions.IgnoreCase)
look like this demo:
void Main()
{
foreach(var f in GetFilesToProcess("c:\\", new[] {".xml", ".txt"}))
Debug.WriteLine(f);
}
private static IEnumerable<string> GetFilesToProcess(string path, IEnumerable<string> extensions)
{
return Directory.GetFiles(path, "*.*")
.Where(f => extensions.Contains(Path.GetExtension(f).ToLower()));
}
#Daniel B, thanks for the suggestion to write my own version of this function. It has the same behavior as Directory.GetFiles, but supports regex filtering.
string[] FindFiles(FolderBrowserDialog dialog, string pattern)
{
Regex regex = new Regex(pattern);
List<string> files = new List<string>();
var files=Directory.GetFiles(dialog.SelectedPath);
for(int i = 0; i < files.Count(); i++)
{
bool found = regex.IsMatch(files[i]);
if(found)
{
files.Add(files[i]);
}
}
return files.ToArray();
}
I found it useful, so I thought I'd share.
c# version of #qfactor77's answer. This is the best way without LINQ .
string[] wildcards= {"*.mp4", "*.jpg"};
ReadOnlyCollection<string> filePathCollection = FileSystem.GetFiles(dirPath, Microsoft.VisualBasic.FileIO.SearchOption.SearchAllSubDirectories, wildcards);
string[] filePath=new string[filePathCollection.Count];
filePathCollection.CopyTo(filePath,0);
now return filePath string array. In the beginning you need
using Microsoft.VisualBasic.FileIO;
using System.Collections.ObjectModel;
also you need to add reference to Microsoft.VisualBasic
I did a simple way for seach as many extensions as you need, and with no ToLower(), RegEx, foreach...
List<String> myExtensions = new List<String>() { ".aspx", ".ascx", ".cs" }; // You can add as many extensions as you want.
DirectoryInfo myFolder = new DirectoryInfo(#"C:\FolderFoo");
SearchOption option = SearchOption.TopDirectoryOnly; // Use SearchOption.AllDirectories for seach in all subfolders.
List<FileInfo> myFiles = myFolder.EnumerateFiles("*.*", option)
.Where(file => myExtensions
.Any(e => String.Compare(file.Extension, e, CultureInfo.CurrentCulture, CompareOptions.IgnoreCase) == 0))
.ToList();
Working on .Net Standard 2.0.
var filtered = Directory.GetFiles(path)
.Where(file => file.EndsWith("aspx", StringComparison.InvariantCultureIgnoreCase) || file.EndsWith("ascx", StringComparison.InvariantCultureIgnoreCase))
.ToList();
Just would like to say that if you use FileIO.FileSystem.GetFiles instead of Directory.GetFiles, it will allow an array of wildcards.
For example:
Dim wildcards As String() = {"*.html", "*.zip"}
Dim ListFiles As List(Of String) = FileIO.FileSystem.GetFiles(directoryyouneed, FileIO.SearchOption.SearchTopLevelOnly, wildcards).ToList
(Sorry to write this as an answer, but I don't have privileges to write comments yet.)
Note that the FileIO.FileSystem.GetFiles() method from Microsoft.VisualBasic is just a wrapper to execute a search for each provided pattern and merge the results.
When checking the source from the .pbd file, you can see from this fragment FileSystem.FindPaths is executed for each pattern in the collection:
private static void FindFilesOrDirectories(
FileSystem.FileOrDirectory FileOrDirectory,
string directory,
SearchOption searchType,
string[] wildcards,
Collection<string> Results)
{
// (...)
string[] strArray = wildcards;
int index = 0;
while (index < strArray.Length)
{
string wildCard = strArray[index];
FileSystem.AddToStringCollection(Results, FileSystem.FindPaths(FileOrDirectory, directory, wildCard));
checked { ++index; }
}
// (...)
}
According to jonathan's answer (for 2 file extensions):
public static string[] GetFilesList(string dir) =>
Directory.GetFiles(dir, "*.exe", SearchOption.AllDirectories)
.Union(Directory.GetFiles(dir, "*.dll", SearchOption.AllDirectories)).ToArray();
Or for more file extensions (search in this folder and subfolders):
public static List<string> GetFilesList(string dir, params string[] fileExtensions) {
List<string> files = new List<string>();
foreach (string fileExtension in fileExtensions) {
files.AddRange(Directory.GetFiles(dir, fileExtension, SearchOption.AllDirectories));
}
return files;
}
List<string> files = GetFilesList("C:\\", "*.exe", "*.dll");
In 3250 files took to find 1890 files takes 0.6 second.

How to check for file names in a folder

I would like to be able to iterate through the name of some image files in a folder using c#. So for intance I have a folder named image and contains the following images
image
dog.jpg
cat.jpg
horse.jpg
I want to be able to go through the names and be able to say
if(filename == dog.jpg)
return true
else
return false
Something of that nature
Thank you
You should use the static Exists method on System.IO.File.
return System.IO.File.Exists("dog.jpg")
Since the method returns a boolean, there is no need for the if statement in the example you gave.
You can also use a bit of Linq magic to determine if a file exists in a folder structure, like this:
var dir = new System.IO.DirectoryInfo(startFolder);
var fileList = dir.GetFiles("*.*", System.IO.SearchOption.AllDirectories);
bool fileExists = fileList.Any(f => f.FullName == "dog.jpg");
or even shorter:
return System.IO.Directory
.GetFiles(#"c:\myfolder", "dog.jpg", SearchOption.AllDirectories)
.Any();
which would search the folder specified and all subfolder with the pattern "dog.jpg". The Any() extension method simply checks whether the IEnumerable contains any items. I think this is the most efficient way of doing this (based on gut feeling).
From http://weblogs.asp.net/israelio/archive/2004/06/23/162913.aspx Just insert your if in the "// do something with filename" area:
// How much deep to scan. (of course you can also pass it to the method)
const int HowDeepToScan=4;
public static void ProcessDir(string sourceDir, int recursionLvl)
{
if (recursionLvl<=HowDeepToScan)
{
// Process the list of files found in the directory.
string [] fileEntries = Directory.GetFiles(sourceDir);
foreach(string fileName in fileEntries)
{
// do something with fileName
Console.WriteLine(fileName);
}
// Recurse into subdirectories of this directory.
string [] subdirEntries = Directory.GetDirectories(sourceDir);
foreach(string subdir in subdirEntries)
// Do not iterate through reparse points
if ((File.GetAttributes(subdir) &
FileAttributes.ReparsePoint) !=
FileAttributes.ReparsePoint)
ProcessDir(subdir,recursionLvl+1);
}
}
get all the files
string[] filePaths = Directory.GetFiles(#"c:\yourfolder\");
and iterate through it
use Directory.GetFiles()
foreach(var file in (myDir.GetFiles("*.jpg")
{
if(file.Name == "dog.jpg") return true;
}
var files = System.IO.Directory.GetFiles("directory", "*.jpg");
foreach (var item in files)
{
if (System.IO.Path.GetFileName(item) == "dog.jpg")
{
// File found.
}
}
DirectoryInfo di = new DirectoryInfo("c:\\Images");
var files = di.GetFiles("*.jpg");
foreach (var fileInfo in files)
{
if (fileInfo.Name == "dog.jpg")
return true;
}
return false;

Can you call Directory.GetFiles() with multiple filters?

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 tried both of the following with no luck:
Directory.GetFiles("C:\\path", "*.mp3|*.jpg", SearchOption.AllDirectories);
Directory.GetFiles("C:\\path", "*.mp3;*.jpg", SearchOption.AllDirectories);
Is there a way to do this in one call?
For .NET 4.0 and later,
var files = Directory.EnumerateFiles("C:\\path", "*.*", SearchOption.AllDirectories)
.Where(s => s.EndsWith(".mp3") || s.EndsWith(".jpg"));
For earlier versions of .NET,
var files = Directory.GetFiles("C:\\path", "*.*", SearchOption.AllDirectories)
.Where(s => s.EndsWith(".mp3") || s.EndsWith(".jpg"));
edit: Please read the comments. The improvement that Paul Farry suggests, and the memory/performance issue that Christian.K points out are both very important.
How about this:
private static string[] GetFiles(string sourceFolder, string filters, System.IO.SearchOption searchOption)
{
return filters.Split('|').SelectMany(filter => System.IO.Directory.GetFiles(sourceFolder, filter, searchOption)).ToArray();
}
I found it here (in the comments): http://msdn.microsoft.com/en-us/library/wz42302f.aspx
If you have a large list of extensions to check you can use the following. I didn't want to create a lot of OR statements so i modified what lette wrote.
string supportedExtensions = "*.jpg,*.gif,*.png,*.bmp,*.jpe,*.jpeg,*.wmf,*.emf,*.xbm,*.ico,*.eps,*.tif,*.tiff,*.g01,*.g02,*.g03,*.g04,*.g05,*.g06,*.g07,*.g08";
foreach (string imageFile in Directory.GetFiles(_tempDirectory, "*.*", SearchOption.AllDirectories).Where(s => supportedExtensions.Contains(Path.GetExtension(s).ToLower())))
{
//do work here
}
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)));
}
Don't forget the new .NET4 Directory.EnumerateFiles for a performance boost (What is the difference between Directory.EnumerateFiles vs Directory.GetFiles?)
"IgnoreCase" should be faster than "ToLower" (.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 )
I know it's old question but LINQ: (.NET40+)
var files = Directory.GetFiles("path_to_files").Where(file => Regex.IsMatch(file, #"^.+\.(wav|mp3|txt)$"));
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();
Another way to use Linq, but without having to return everything and filter on that in memory.
var files = Directory.GetFiles("C:\\path", "*.mp3", SearchOption.AllDirectories).Union(Directory.GetFiles("C:\\path", "*.jpg", SearchOption.AllDirectories));
It's actually 2 calls to GetFiles(), but I think it's consistent with the spirit of the question and returns them in one enumerable.
Let
var set = new HashSet<string>(
new[] { ".mp3", ".jpg" },
StringComparer.OrdinalIgnoreCase); // ignore case
var dir = new DirectoryInfo(path);
Then
dir.EnumerateFiles("*.*", SearchOption.AllDirectories)
.Where(f => set.Contains(f.Extension));
or
from file in dir.EnumerateFiles("*.*", SearchOption.AllDirectories)
from ext in set // makes sense only if it's just IEnumerable<string> or similar
where String.Equals(ext, file.Extension, StringComparison.OrdinalIgnoreCase)
select file;
Nope. Try the following:
List<string> _searchPatternList = new List<string>();
...
List<string> fileList = new List<string>();
foreach ( string ext in _searchPatternList )
{
foreach ( string subFile in Directory.GetFiles( folderName, ext )
{
fileList.Add( subFile );
}
}
// Sort alpabetically
fileList.Sort();
// Add files to the file browser control
foreach ( string fileName in fileList )
{
...;
}
Taken from: http://blogs.msdn.com/markda/archive/2006/04/20/580075.aspx
I can't use .Where method because I'm programming in .NET Framework 2.0 (Linq is only supported in .NET Framework 3.5+).
Code below is not case sensitive (so .CaB or .cab will be listed too).
string[] ext = new string[2] { "*.CAB", "*.MSU" };
foreach (string found in ext)
{
string[] extracted = Directory.GetFiles("C:\\test", found, System.IO.SearchOption.AllDirectories);
foreach (string file in extracted)
{
Console.WriteLine(file);
}
}
List<string> FileList = new List<string>();
DirectoryInfo di = new DirectoryInfo("C:\\DirName");
IEnumerable<FileInfo> fileList = di.GetFiles("*.*");
//Create the query
IEnumerable<FileInfo> fileQuery = from file in fileList
where (file.Extension.ToLower() == ".jpg" || file.Extension.ToLower() == ".png")
orderby file.LastWriteTime
select file;
foreach (System.IO.FileInfo fi in fileQuery)
{
fi.Attributes = FileAttributes.Normal;
FileList.Add(fi.FullName);
}
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"))
{}
DirectoryInfo directory = new DirectoryInfo(Server.MapPath("~/Contents/"));
//Using Union
FileInfo[] files = directory.GetFiles("*.xlsx")
.Union(directory
.GetFiles("*.csv"))
.ToArray();
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.
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);
}
What about
string[] filesPNG = Directory.GetFiles(path, "*.png");
string[] filesJPG = Directory.GetFiles(path, "*.jpg");
string[] filesJPEG = Directory.GetFiles(path, "*.jpeg");
int totalArraySizeAll = filesPNG.Length + filesJPG.Length + filesJPEG.Length;
List<string> filesAll = new List<string>(totalArraySizeAll);
filesAll.AddRange(filesPNG);
filesAll.AddRange(filesJPG);
filesAll.AddRange(filesJPEG);
Just found an another way to do it. Still not one operation, but throwing it out to see what other people think about it.
private void getFiles(string path)
{
foreach (string s in Array.FindAll(Directory.GetFiles(path, "*", SearchOption.AllDirectories), predicate_FileMatch))
{
Debug.Print(s);
}
}
private bool predicate_FileMatch(string fileName)
{
if (fileName.EndsWith(".mp3"))
return true;
if (fileName.EndsWith(".jpg"))
return true;
return false;
}
I wonder why there are so many "solutions" posted?
If my rookie-understanding on how GetFiles works is right, there are only two options and any of the solutions above can be brought down to these:
GetFiles, then filter: Fast, but a memory killer due to storing overhead untill the filters are applied
Filter while GetFiles: Slower the more filters are set, but low memory usage as no overhead is stored.This is explained in one of the above posts with an impressive benchmark: Each filter option causes a seperate GetFile-operation so the same part of the harddrive gets read several times.
In my opinion Option 1) is better, but using the SearchOption.AllDirectories on folders like C:\ would use huge amounts of memory.
Therefor i would just make a recursive sub-method that goes through all subfolders using option 1)
This should cause only 1 GetFiles-operation on each folder and therefor be fast (Option 1), but use only a small amount of memory as the filters are applied afters each subfolders' reading -> overhead is deleted after each subfolder.
Please correct me if I am wrong. I am as i said quite new to programming but want to gain deeper understanding of things to eventually become good at this :)
Here is a simple and elegant way of getting filtered files
var allowedFileExtensions = ".csv,.txt";
var files = Directory.EnumerateFiles(#"C:\MyFolder", "*.*", SearchOption.TopDirectoryOnly)
.Where(s => allowedFileExtensions.IndexOf(Path.GetExtension(s)) > -1).ToArray();
Make the extensions you want one string i.e ".mp3.jpg.wma.wmf" and then check if each file contains the extension you want.
This works with .net 2.0 as it does not use LINQ.
string myExtensions=".jpg.mp3";
string[] files=System.IO.Directory.GetFiles("C:\myfolder");
foreach(string file in files)
{
if(myExtensions.ToLower().contains(System.IO.Path.GetExtension(s).ToLower()))
{
//this file has passed, do something with this file
}
}
The advantage with this approach is you can add or remove extensions without editing the code i.e to add png images, just write myExtensions=".jpg.mp3.png".
/// <summary>
/// Returns the names of files in a specified directories that match the specified patterns using LINQ
/// </summary>
/// <param name="srcDirs">The directories to seach</param>
/// <param name="searchPatterns">the list of search patterns</param>
/// <param name="searchOption"></param>
/// <returns>The list of files that match the specified pattern</returns>
public static string[] GetFilesUsingLINQ(string[] srcDirs,
string[] searchPatterns,
SearchOption searchOption = SearchOption.AllDirectories)
{
var r = from dir in srcDirs
from searchPattern in searchPatterns
from f in Directory.GetFiles(dir, searchPattern, searchOption)
select f;
return r.ToArray();
}
Nop... I believe you have to make as many calls as the file types you want.
I would create a function myself taking an array on strings with the extensions I need and then iterate on that array making all the necessary calls. That function would return a generic list of the files matching the extensions I'd sent.
Hope it helps.
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");
Using GetFiles search pattern for filtering the extension is not safe!!
For instance you have two file Test1.xls and Test2.xlsx and you want to filter out xls file using search pattern *.xls, but GetFiles return both Test1.xls and Test2.xlsx
I was not aware of this and got error in production environment when some temporary files suddenly was handled as right files. Search pattern was *.txt and temp files was named *.txt20181028_100753898
So search pattern can not be trusted, you have to add extra check on filenames as well.
Or you can just convert the string of extensions to String^
vector <string> extensions = { "*.mp4", "*.avi", "*.flv" };
for (int i = 0; i < extensions.size(); ++i)
{
String^ ext = gcnew String(extensions[i].c_str());;
String^ path = "C:\\Users\\Eric\\Videos";
array<String^>^files = Directory::GetFiles(path,ext);
Console::WriteLine(ext);
cout << " " << (files->Length) << endl;
}
i don t know what solution is better, but i use this:
String[] ext = "*.ext1|*.ext2".Split('|');
List<String> files = new List<String>();
foreach (String tmp in ext)
{
files.AddRange(Directory.GetFiles(dir, tmp, SearchOption.AllDirectories));
}
you can add this to your project
public static class Collectables {
public static List<System.IO.FileInfo> FilesViaPattern(this System.IO.DirectoryInfo fldr, string pattern) {
var filter = pattern.Split(" ");
return fldr.GetFiles( "*.*", System.IO.SearchOption.AllDirectories)
.Where(l => filter.Any(k => l.Name.EndsWith(k))).ToList();
}
}
then use it anywhere like this
new System.IO.DirectoryInfo("c:\\test").FilesViaPattern("txt doc any.extension");

Categories