Directory.EnumerateFiles method: How to enumerate files in specified subdirectories? - c#

Assume the following directory structure. "C:\Genre" and the "Genre" directory having several subdirectories: "Rock", "Pop", "Metal", "Jazz".
How would I tweak the following statement to search in "Rock" and "Metal" subdirectories only?
var myFiles = Directory.
EnumerateFiles("C:\Genre", "*", SearchOption.AllDirectories).
Where(s => s.EndsWith(".jpg") || s.EndsWith(".gif"));

I'd say the clean way would be search each directory you want, and then add them up in the myFiles
var rockFiles = Directory.EnumerateFiles("c:\Genre\Rock", "*", SearchOption.AllDirectories).Where(s => s.EndsWith(".jpg") || s.EndsWith(".gif"));
var metalFiles = Directory.EnumerateFiles("c:\Genre\Metal", "*", SearchOption.AllDirectories).Where(s => s.EndsWith(".jpg") || s.EndsWith(".gif"));
var myFiles = RockFiles.Concat(MetalFiles);
Now, if you want a generic way to do so, that would be a different story, and a bit more complex one :)

Please consider some implementation like this one:
public static class Program
{
public static void Main()
{
var directoryPaths = new List<string>
{
#"C:\root\path_1",
#"C:\root\path_2",
#"C:\root\path_3"
// …
};
var searchPatterns = new List<string>
{
"*.jpg",
"*.gif"
};
var filePaths = directoryPaths
.SelectMany(directoryPath =>
EnumerateFiles(directoryPath, searchPatterns, SearchOption.AllDirectories))
.ToList()
.AsReadOnly();
// …
}
private static IEnumerable<string> EnumerateFiles(
string path,
IEnumerable<string> searchPatterns,
SearchOption searchOption)
{
var filePaths = searchPatterns.SelectMany(
searchPattern => Directory.EnumerateFiles(path, searchPattern, searchOption));
return filePaths;
}
}

Use Concat in Linq to combine the files in two directories.
var rockFiles = Directory.
EnumerateFiles("C:\Genre\Rock", "*", SearchOption.AllDirectories);
var metalFiles = Directory.
EnumerateFiles("C:\Genre\Metal", "*", SearchOption.AllDirectories);
var myFiles = rockFile
.Concat(metalFiles)
.Where(s => s.EndsWith(".jpg") || s.EndsWith(".gif"));

You could use two arrays for the extensions and the Path-class:
var myFiles = Directory.EnumerateFiles(#"C:\Temp\Genre", "*", SearchOption.AllDirectories)
.Where(fn => genres.Contains(Path.GetFileName(Path.GetDirectoryName(fn)), StringComparer.InvariantCultureIgnoreCase)
&& extensions.Contains(Path.GetExtension(fn), StringComparer.InvariantCultureIgnoreCase));
Path.GetFileName(Path.GetDirectoryName(fn)) returns the folder-name of a file-path.

Related

Looking for folders in multiple extension and multiple string format

Hi I am trying to get all the files with a set of extension and a set of string format
string extensions=".exe,.txt,.xls";
string fileFormat"fileA, fileB, fileC";
let says if i have the following files in the folder
fileA20200805.txt
fileBxxxx.exe
FileCCCCCCC.txt
FileD123.xls
the result should only return the first 3 files which is
fileA20200805.txt
fileBxxxx.exe
FileCCCCCCC.txt
because FileD123.xls is not in the fileFormat.
I have tried the following code:
Directoryinfo dInfo = new DirectoryInfo(path);
FileInfo[] files = dInfoGetFiles()
.Where(f => extensions.Contains(f.Extension.ToLower()) && fileFormat.Any(f.Name.Contains))
.ToArray();
However, I am still getting all 4 files, the FileD123.xls is still returning
Maybe
var extensions = new [] {".exe",".txt",".xls"};
var fileFormat = new [] {"fileA", "fileB", "fileC"};
...
.Where(f =>
extensions.Contains(f.Extension.ToLower()) &&
fileFormat.Any(x => f.Name.StartsWith(x, StringComparison.OrdinalIgnoreCase)))
You could also use regex i guess
var regex = new Regex(#$"({string.Join("|", fileFormat)}[^.]*({string.Join(" | ", extensions)})", RegexOptions.Compiled|RegexOptions.IgnoreCase);
...
.Where(f => regex.IsMatch(f.Name))
I think this should work.
string[] extensions = new string[] { ".exe",".txt",".xls" };
string[] fileFormat = new string[] { "fileA", "fileB", "fileC" };
DirectoryInfo dInfo = new DirectoryInfo(path);
FileInfo[] files = dInfo.GetFiles();
var output = files.Where(f => extensions.Contains(f.Extension.ToLower()) &&
fileFormat.Any(f.Name.Contains)).ToArray();
it return 2 because FileCCCCCCC dont equals fileC.

Return a list of Directory (Folder) names that match a partial string

I need someone to point me in the right direction.
Goal:
Return a list of Folder Names in a path that contain a string in their name. For example: The Path has a Directory named Pictures_New and Videos_New. The string I am searching with is "Pictures_" and "Videos_".
It all works with one string parameter being passed as a search string. My problem is getting it to work with multiple filters. I know it is easily done with file names and extensions.
This is being passed to GetFolders():
string[] filterStrings = { "Pictures_", "Videos_" }
Rest of my code:
public IEnumerable<string> GetFolders(string path, string[] filterStrings, SearchOption searchOption = SearchOption.AllDirectories)
{
IEnumerable<string> folders = Directory.EnumerateDirectories(path, "Pictures_*.*", searchOption);
var resultFolders = new List<string>();
if(filterStrings.Length > 0)
{
foreach (var foldername in folders)
{
string folderName = Path.GetFileName(Path.GetDirectoryName(foldername));
if (string.IsNullOrEmpty(folderName) || Array.IndexOf(filterStrings, "*" + folderName) < 0)
{
// This leaves us only with the Directory names. No paths.
var b = (foldername.Substring(foldername.LastIndexOf(#"\") + 1));
resultFolders.Add(b);
}
}
}
return resultFolders;
}
You can use Linq SelectMany to parse your list of filters and return a list of the results with Directory.GetDirectories();
It will of course return all the Sub Directories that match the filter. Use just "*".
public IEnumerable<string> GetFolders(string path, string[] filterStrings, SearchOption searchOption = SearchOption.AllDirectories)
{
List<string> resultFolders = filterStrings
.SelectMany(flt => Directory.GetDirectories(path, flt, searchOption))
.ToList();
return resultFolders;
}
try:
var patterns = new[] { "Pictures_*", "Videos_*" };
var dirsFound = new List<string>();
foreach (var dir in patterns.Select(pattern => Directory.GetDirectories(#"my path", pattern).ToArray()))
{
dirsFound.AddRange(dir);
}
Looks like you're not looping through each of your filter strings:
var folders = new List<string>();
foreach (var filterString in filterStrings)
{
folders.AddRange(Directory.EnumerateDirectories(path, filterString, searchOption););
}

Search file in Directory using partial name in C#

I need to search file in directory using partial name.
Ex:
Directory : c:\Path
Filename : Error_005296-895632-12563.xml
Partial file name: 005296-895632-12563
I have tried below.
Directory.GetFiles("c:\Path", "*005296-895632-12563*.xml", SearchOption.AllDirectories).
But it didn't work
Sample file names are :
Error_005296-895632-12563.xml
005296-895632-12563_Response.xml
Duplicate_005296-895632-12563_Response.xml
You can create a extension method and pass the array of partial names which you want to find.
Call the extension method like this
DirectoryInfo dir = new DirectoryInfo(#"c:\demo");
FileInfo[] files = dir.GetFilesBypartialName("Anc_def_", "ABC_123", "12_qweqweqw_123").ToArray();
below is the extension method
public static class DirectoryFindFile
{
public static IEnumerable<FileInfo> GetFilesBypartialName(this DirectoryInfo dirInfo, params string[] partialFilenames)
{
if (partialFilenames == null)
throw new ArgumentNullException("partialFilenames");
var lstpartialFilenames = new HashSet<string>(partialFilenames, StringComparer.OrdinalIgnoreCase);
return dirInfo.EnumerateFiles()
.Where(f => lstpartialFilenames.Contains(f.Name));
}
public static IEnumerable<FileInfo> GetFilesBypartialFilenamesAllDir(this DirectoryInfo dirInfo, params string[] partialFilenames)
{
if (partialFilenames == null)
throw new ArgumentNullException("partialFilenames");
var lstpartialFilenames = new HashSet<string>(partialFilenames, StringComparer.OrdinalIgnoreCase);
return dirInfo.EnumerateFiles("*.*", SearchOption.AllDirectories)
.Where(f => lstpartialFilenames.Contains(f.Name));
}
}
Heres a extension function I've used to search directories
public static IEnumerable<string> GetFiles(string path, string searchPatternExpression = "", SearchOption searchOption = SearchOption.AllDirectories)
{
Regex reSearchPattern = new Regex(searchPatternExpression);
return Directory.EnumerateFiles(path, "*", searchOption).Where(file => reSearchPattern.IsMatch(System.IO.Path.GetExtension(file)));
}

Find First File in a Branching Directory

I'm trying to find the first .dcm in a directory tree then get the first full path (a/a/a/123.dcm) . However ignoring directories where the ie .dcm is not found.
example:
a/a/a/123.dcm
a/a/a/1234.dcm
a/a/a/12345.dcm
a/a/b/23.dcm
a/a/b/234.dcm
a/a/b/2345.dcm
a/a/c/23.dcm
a/a/c/234.dcm
a/a/c/2345.dcm
Answer should be: a/a/a/123.dcm, a/a/b/23.dcm and a/a/c/23.dcm
I tried:
var files = Directory.GetFiles(inputDir, "*.*", SearchOption.AllDirectories)
.Where(s => s.EndsWith(".dcm")).ToArray();
var dir = Directory.GetDirectories(inputDir, "*.*", SearchOption.AllDirectories).ToArray();
var biggest = files.First();
foreach (var item in dir)
{
DirectoryInfo di = new DirectoryInfo(item);
var q = from i in di.GetFiles("*.dcm", SearchOption.AllDirectories)
select i.Name;
var qq = q.First();
foreach (var items in qq)
{
Console.WriteLine(items);
}
}
However what I get is the answer for five directories. Answer:
a/a/a/123.dcm
a/a/a/123.dcm
a/a/a/123.dcm
a/a/b/23.dcm
a/a/c/23.dcm
I’m just wondering if there’s a simpler way to do this using LINQ or something else? Thank you so much for your help. Cheers.
Here's a LINQ version:
var inputDir = #"c:\\temp";
var files = Directory
.EnumerateFiles(inputDir, "*.dcm", SearchOption.AllDirectories)
.Select(f => new FileInfo(f))
.GroupBy(f => f.Directory.FullName, d => d, (d, f) => new { Directory = d, FirstFile = f.ToList().First() })
.ToList();
files.ForEach(f => Console.WriteLine("{0} {1}", f.Directory, f.FirstFile));

recursively scan all the directories under the root directory and find only the newest file from each folder

that's what i try but it return only the newest file from only the top directories under the root
if(Directory.Exists("YourPath"))
foreach (string _tempFiles in Directory.GetDirectories("YourPath")
.Select(directory => Directory.GetFiles(directory, "*.*", SearchOption.AllDirectories)
.OrderByDescending(File.GetLastWriteTime)
.FirstOrDefault()))
This returns all newest files of each directory(including root):
var rootDirFile = Directory
.EnumerateFiles(yourPath, "*.*", SearchOption.TopDirectoryOnly)
.OrderByDescending(f => File.GetLastWriteTime(f))
.Take(1);
var allNewestFilesOfEachFolder = Directory
.EnumerateDirectories(yourParth, "*.*", SearchOption.AllDirectories)
.Select(d => Directory.EnumerateFiles(d, "*.*")
.OrderByDescending(f => File.GetLastWriteTime(f))
.FirstOrDefault());
// put both together, the root-file first
allNewestFilesOfEachFolder = rootDirFile.Concat(allNewestFilesOfEachFolder);
If there's no file in a directory the file is null, so the number of files is equal to the number of folders.
Note that Linq is not the right tool for System.IO since error-handling is difficult.
I wrote a basic recursive function to handle this:
// Dictionary:
// Key = The directory name.
// Value = The most recently modified file for that directory.
public static Dictionary<string, string> GetNewestFiles(string directory)
{
return GetNewestFiles(directory, null);
}
static Dictionary<string, string> GetNewestFiles(string directory,
Dictionary<string, string> dictionary)
{
if(dictionary == null)
dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
try
{
var files = from file in Directory.GetFiles(directory)
select new FileInfo(file);
var latestFile = files.OrderByDescending(file => { return file.LastWriteTimeUtc; }).FirstOrDefault();
if (latestFile != null)
dictionary[latestFile.DirectoryName] = latestFile.FullName;
}
catch { }
foreach (var subDirectory in Directory.GetDirectories(directory))
{
try
{
GetNewestFiles(subDirectory, dictionary);
}
catch { }
}
return dictionary;
}
So then you can just call it like so:
var fileDictionary = GetNewestFiles(#"C:\MyFolder");

Categories

Resources