I want to return a list of all the subdirectories in the 'SomeFolder' directory excluding the 'Admin' and 'Templates' directories.
I have the following folder structure (simplified):
C:\inetpub\wwwroot\MyWebsite\SomeFolder\RandomString
C:\inetpub\wwwroot\MyWebsite\SomeFolder\RandomString
C:\inetpub\wwwroot\MyWebsite\SomeFolder\RandomString
C:\inetpub\wwwroot\MyWebsite\SomeFolder\Admin
C:\inetpub\wwwroot\MyWebsite\SomeFolder\Templates
'SomeFolder' can contain a varying number a 'RandomString' folders (anywhere from ~10 to ~100).
Here is what I have tried:
var dirs = Directory.GetDirectories(Server.MapPath(".."))
.Where(s => !s.EndsWith("Admin") || !s.EndsWith("Templates"));
foreach (string dir in dirs)
{
lit.Text += Environment.NewLine + dir;
}
This returns the full list of folders (shown above) without 'Admin' and 'Templates' filtered out.
Interestingly, if I change the LINQ .Where clause to include, instead of exclude, 'Admin' and 'Templates' it works, meaning it returns just the paths for 'Admin' and 'Templates'.
.Where(s => s.EndsWith("Admin") || s.EndsWith("Templates"));
If LINQ is not the solution, is there any way to use the GetDirectories SearchPattern to filter out directories?
You can do something like:
//list your excluded dirs
private List<string> _excludedDirectories= new List<string>() { "Admin", "Templates" };
//method to check
static bool isExcluded(List<string> exludedDirList, string target)
{
return exludedDirList.Any(d => new DirectoryInfo(target).Name.Equals(d));
}
//then use this
var filteredDirs = Directory.GetDirectories(path).Where(d => !isExcluded(_excludedDirectories, d));
the opposite of (A || B) is (!A && !B), so in your code it should be &&, not ||...
Related
I am trying to get a list of FileInfo objects that satisfy multiple filters.
Every suggestion I have seen uses array of file names/paths instead of FileInfo:
var files = Directory.GetFiles(sLogPath, "*.*", SearchOption.TopDirectoryOnly)
.Where(s => s.StartsWith("abc", StringComparison.CurrentCultureIgnoreCase) || s.StartsWith("def", StringComparison.CurrentCultureIgnoreCase));
What I am trying to get is:
DirectoryInfo di = new DirectoryInfo(sLogPath);
var files = di.GetFiles(<same filter as above>);
But it looks like I can only do something like:
var files = di.GetFiles("*_" + dateStr + ".log");
Based on your comment to me on your question, it looks like you want to filter on file names, but get the FileInfos that correspond to these names.
You can do something like this
var di = new DirectoryInfo(sLogPath);
var files = di
.GetFiles("*.*", SearchOption.TopDirectoryOnly)
.Where(x => x.Name.StartsWith("abc", StringComparison.CurrentCultureIgnoreCase)
|| x.Name.StartsWith("def", StringComparison.CurrentCultureIgnoreCase))
.ToList();
We're using the Name property in the filter and working with the FileInfo[] array returned by DirectoryInfo.GetFiles().
Imagine I have this code
public List<string> X
and I load the following items:
launch.txt
reset.txt
foldername
otherfoldername
I know I can find if an item is on that list by calling X.Contains("value")
but what if I pass "foldername/file.txt".
What's the easiest way to check if a string starts with any of the entries on the X list?
Ideally I want to catch all files that are inside "foldername/." and subdirectories too, so I thought to use the StartWith.
Is LINQ the right approach for this?
Use the Enumerable.Any extension method, which returns true if and only if there is some item in the sequence for which the given predicate returns true.
string search = "foldername/file.txt";
bool result = X.Any(s => search.StartsWith(s));
Of course, StartsWith might not actually be appropriate for your scenario. What if there were only a folder named foldername2 in X? You wouldn't want result to be true in that case, I suspect.
If you want to get the items in X that match the search, you can do the following.
string search = "foldername/file.txt";
IEnumerable<string> result = X.Where(s => search.StartsWith(s));
If you want to get the first item in X that matches the search, you can do the following.
string search = "foldername/file.txt";
string result = X.FirstOrDefault(s => search.StartsWith(s));
Use the Path class if you are fiddling around with paths:
List<string> X = new List<string>(){
"launch.txt","reset.txt","foldername","otherfoldername"
};
string search = "foldername/file.tx";
var searchInfo = new
{
FileNameWoe = Path.GetFileNameWithoutExtension(search),
FileName = Path.GetFileName(search),
Directory = Path.GetDirectoryName(search)
};
IEnumerable<String> matches = X.Select(x => new
{
str = x,
FileNameWoe = Path.GetFileNameWithoutExtension(x),
FileName = Path.GetFileName(x),
Directory = Path.GetDirectoryName(x)
}).Where(xInfo => searchInfo.FileName == xInfo.FileNameWoe
|| searchInfo.FileNameWoe == xInfo.FileName
|| searchInfo.Directory == xInfo.Directory
|| searchInfo.Directory == xInfo.FileNameWoe
|| searchInfo.FileNameWoe == xInfo.Directory)
.Select(xInfo => xInfo.str)
.ToList();
Finds: foldername because one of the filename's FileNameWithoutExtension equals the directory of the path you're searching.
I have 140 directories that I'm trying to process. According to my tests there are 139 directories that match my file pattern (*abc.txt).
I'm trying to find the 1 directory to verify that in fact it does not have a *abc.txt in it.
How can I do this?
The following code gives me the 140 directories number:
var directoryCount = from subdirectory in Directory.GetDirectories(paramStartFilePath, "*", SearchOption.AllDirectories)
where Directory.GetDirectories(subdirectory).Length == 0
select subdirectory;
I'm gathering the files based off the pattern like this:
dirInfoFiles= new DirectoryInfo(startFilePath);
IEnumerable<FileInfo> listFiles = dirInfoFiles.EnumerateFiles("*abc.txt, System.IO.SearchOption.AllDirectories);
How can I find the the one directory that doesn't contain my .txt file?
There is always the running the tank through the village approach: just enumerate *.* and then exclude the patterns that don't match.
If you want all directories that does not contain at least one txt-file which name ends with "abc":
IEnumerable<DirectoryInfo> matchingDirs = dirInfoFiles.EnumerateDirectories("*.*", System.IO.SearchOption.AllDirectories)
.Where(d => !d.EnumerateFiles().Any(f => f.Extension.ToUpper() == ".TXT"
&& f.Name.EndsWith("abc", StringComparison.OrdinalIgnoreCase)));
or the same in other words, possibly more readable:
IEnumerable<DirectoryInfo> matchingDirs = dirInfoFiles
.EnumerateDirectories("*.*", System.IO.SearchOption.AllDirectories)
.Where(d => !d.EnumerateFiles("*abc.txt").Any());
Here is my take. It returns the first item (or null) that contains a file ending with the text you are looking for and is case insensitive. You could remove the lambdas to make it more readable.
var directory = Directory.GetDirectories((paramStartFilePath, "*", SearchOption.AllDirectories)
.FirstOrDefault(x => new DirectoryInfo(x).EnumerateFiles().Any(f => !f.Name.EndsWith("abc.txt",true,CultureInfo.CurrentCulture)));
I'm creating a tool that is supposed to concatenate docs that contain the same name.
example: C_BA_20000_1.pdf and C_BA_20000_2.pdf
These files should be grouped in one list.
That tool runs on a directory lets say
//directory of pdf files
DirectoryInfo dirInfo = new DirectoryInfo(#"C:\Users\derp\Desktop");
FileInfo[] fileInfos = dirInfo.GetFiles("*.pdf");
foreach (FileInfo info in fileInfos)
I want to create an ArrayList that contains filenames of the same name
ArrayList list = new ArrayList();
list.Add(info.FullName);
and then have a list that contains all the ArrayLists of similar docs.
List<ArrayList> bigList = new List<ArrayList>();
So my question, how can I group files that contains same name and put them in the same list.
EDIT:
Files have the same pattern in their names AB_CDEFG_i
where i is a number and can be from 1-n. Files with the same name should have only different number at the end.
AB_CDEFG_1
AB_CDEFG_2
HI_JKLM_1
Output should be:
List 1: AB_CDEFG_1 and AB_CDEFG_2
List 2: HI_JKLM_1
Create method which extracts 'same' part of file name. E.g.
public string GetRawName(string fileName)
{
int index = fileName.LastIndexOf("_");
return fileName.Substring(0, index);
}
And use this method for grouping:
var bigList = Directory.EnumerateFiles(#"C:\Users\derp\Desktop", "*.pdf")
.GroupBy(file => GetRawName(file))
.Select(g => g.ToList())
.ToList();
This will return List<List<string>> (without ArrayList).
UPDATE Here is regular expression, which will work with all kind of files, whether they have number at the end, or not
public string GetRawName(string file)
{
string name = Path.GetFileNameWithoutExtension(file);
return Regex.Replace(name, #"(_\d+)?$", "")
}
Grouping:
var bigList = Directory.EnumerateFiles(#"C:\Users\derp\Desktop", "*.pdf")
.GroupBy(GetRawName)
.Select(g => g.ToList())
.ToList();
It sounds like the difficulty is in deciding which files are the same.
static string KeyFromFileName(string file)
{
// Convert from "C_BA_20000_2" to "C_BA_20000"
return file.Substring(0, file.LastIndexOf("_"));
// Note: This assumes there is an _ in the filename.
}
Then you can use this LINQ to build a list of fileSets.
using System.Linq; // Near top of file
var files = Directory.GetFiles(#"C:\Users\derp\Desktop", "*.pdf")
var fileSets = files
.Select(file => file.FullName)
.GroupBy(KeyFromFileName)
.Select(g => new {g.Key, Files = g.ToList()}
.ToList();
Aside from the fact that your question doesnt identify what "same name" means. This is a typical solution.
fileInfos.GroupBy ( f => f.FullName )
.Select( grp => grp.ToList() ).ToList();
This will get you a list of lists... also won't throw an exception if a file doesn't contain the underscore, etc.
private string GetKey(FileInfo fi)
{
var index = fi.Name.LastIndexOf('_');
return index == -1 ? Path.GetFileNameWithoutExtension(fi.Name)
: fi.Name.Substring(0, index);
}
var bigList = fileInfos.GroupBy(GetKey)
.Select(x => x.ToList())
.ToList();
I got the following code :
foreach (string file in Directory.GetFiles(generationDir, "*.xaml")
{
File.Delete(file);
}
foreach (string file in Directory.GetFiles(generationDir, "*.cs")
{
File.Delete(file);
}
I'd like to merge these 2 foreach in a single loop ? Is that possible ?
I'm not comfortable with lambda expression so got a hard time here...
foreach (var file in
Directory.GetFiles(generationDir, "*.*")
.Where(item => item.EndsWith(".xaml") || item.EndsWith(".cs")))
{
File.Delete(file);
}
For those who like ForEach() "sequence operator fake" :)
(Please be aware of “foreach” vs “ForEach” before usign it)
Directory.GetFiles(generationDir, "*.*")
.Where(item => item.EndsWith(".xaml") || item.EndsWith(".cs"))
.ToList()
.ForEach(item => File.Delete(item))
If you are usign .NET Framework v4 you can use more efficient Directory.EnumerateFiles() method:
The EnumerateFiles and GetFiles methods differ as follows: When you
use EnumerateFiles, you can start enumerating the collection of names
before the whole collection is returned; when you use GetFiles, you
must wait for the whole array of names to be returned before you can
access the array. Therefore, when you are working with many files and
directories, EnumerateFiles can be more efficient.
The returned collection is not cached; each call to the GetEnumerator
on the collection will start a new enumeration.
You can just union the two sets
var items = Directory.GetFiles(generationDir, "*.xaml").Where(item => item.EndsWith(".xaml"))
.Union(Directory.GetFiles(generationDir, "*.cs").Where(item => item.EndswWith(".cs"))
Then foreach through them.
By the way, the where clause on each of the collections seems to be redundant to me, since GetFiles uses the mask you provided, so all of the files should end in .xaml or .cs.
How about with LINQ:
var files = from extension in new[] { "xaml", "cs" }
from fileName in Directory.GetFiles(generationDir, "*." + extension)
select fileName;
foreach(var file in files)
File.Delete(file);
Or in method syntax:
var files = new[] { "xaml", "cs" }
.SelectMany(extension => Directory.GetFiles(generationDir, "*." + extension))
foreach(var file in files)
File.Delete(file);
Remove the filter argument from the GetFiles method call and use and or in your where clause:
var files = Directory.GetFiles(generationDir).Where(item => item.EndsWith(".cs") || item.EndsWith(".xaml"));
foreach (var file in files)
{
File.Delete(file);
}
var filenames = (new[] { "*.cs", "*.xaml" }).SelectMany(
ext => Directory.GetFiles(generationDir, ext));
foreach(var filename in filenames)
File.Delete(filename);
I also removed the redundant Where statements since you were double filtering on the same criteria as you passed in to Directory.GetFiles.
try like this using this operator ||
foreach (string file in Directory.GetFiles(generationDir, "*.*")
.Where(item => item.EndsWith(".xaml") || item.EndsWith(".cs"))
{
File.Delete(file);
}