I'm working on a program that is supposed to scan a specific directory looking for any directories within it that have specific names, and if it finds them, tell the user.
Currently, the way I am loading the names its searching for is like this:
static string path = Path.Combine(Directory.GetCurrentDirectory(), #"database.txt");
static string[] database = File.ReadAllLines(datapath);
I am using this as an array of names to look for when looking through a specific directory. I am doing so with a foreach method.
System.IO.DirectoryInfo di = new DirectoryInfo("C:\ExampleDirectory");
foreach (DirectoryInfo dir in di.GetDirectories())
{
}
Is there a way to see if any of the names in the file "database.txt" match any names of directories found within "C:\ExampleDirectory"?
The only way I can think of doing this is:
System.IO.DirectoryInfo di = new DirectoryInfo(versionspath);
foreach (DirectoryInfo dir in di.GetDirectories())
{
if(dir.Name == //Something...) {
Console.WriteLine("Match found!");
break;}
}
But this obviously won't work, and I cannot think of any other way to do this. Any help would be greatly appreciated!
Based on your other questions on stackoverflow, I presume your question is a homework or you are a passionate hobby programmer, am I right? So I'll try to explain the principle here continuing your almost complete solution.
You will need a nested loop here, a loop in a loop. In the outer loop you iterate through the directories. You already got this one. For each directory you need to loop through the names in database to see if any item in it matches the name of the directory:
System.IO.DirectoryInfo di = new DirectoryInfo(versionspath);
foreach (DirectoryInfo dir in di.GetDirectories())
{
foreach (string name in database)
{
if (dir.Name == name)
{
Console.WriteLine("Match found!");
break;
}
}
}
Depending on your goal, you might want to exit at the first matching directory. The sample code above doesn't. The single break; statement only exits the inner loop, not the outer one. So it continues to check the next directory. Try to figure it out yourself how to stop at the first match (by exiting the outer loop).
As usual, LINQ is the way to go. Whenever you have to find matches or not-matches between two lists and both lists containing different types, you'll have to use .Join() or .GroupJoin().
The .Join() comes into play, if you need to find a 1:1 relationship and the .GroupJoin() for any kind of 1-to relationship (1:0, 1:many or also 1:1).
So, if you need the directories that match your list, this sounds for a job to the .Join() operator:
public static void Main(string[] args)
{
// Where ever this comes normally from.
string[] database = new[] { "fOo", "bAr" };
string startDirectory = #"D:\baseFolder";
// A method that returns an IEnumerable<string>
// Using maybe a recursive approach to get all directories and/or files
var candidates = LoadCandidates(startDirectory);
var matches = database.Join(
candidates,
// Simply pick the database entry as is.
dbEntry => dbEntry,
// Only take the last portion of the given path.
fullPath => Path.GetFileName(fullPath),
// Return only the full path from the given matching pair.
(dbEntry, fullPath) => fullPath,
// Ignore case on comparison.
StringComparer.OrdinalIgnoreCase);
foreach (var match in matches)
{
// Shows "D:\baseFolder\foo"
Console.WriteLine(match);
}
Console.ReadKey();
}
private static IEnumerable<string> LoadCandidates(string baseFolder)
{
return new[] { #"D:\baseFolder\foo", #"D:\basefolder\baz" };
//return Directory.EnumerateDirectories(baseFolder, "*", SearchOption.AllDirectories);
}
You can use LINQ to do this
var allDirectoryNames = di.GetDirectories().Select(d => d.Name);
var matches = allDirectoryNames.Intersect(database);
if (matches.Any())
Console.WriteLine("Matches found!");
In the first line we get all the directory names, then we use the Intersect() method to see which ones are present in both allDirectoryNames and database
Related
I am looking for a library that allows me to take something similar to this:
./*
./*/*.*
./**
./**/*.*
./*.cs
./folder1/*.png
Then i would need to pass it to a method which scans the filesystem and returns these paths:
C:\folder1, C:\folder2, C:\folder3
C:\folder1\file.cs,C:\folder1\test.dll,[...], C:\folder2\image.png, [...]
C:\folder1, C:\folder1\results, C:\folder2, [...]
C:\file1.cs, C:\file2.cs, C:\file3.cs
C:\folder1\image1.png, C:\folder1\image2.png, [...]
I am aware that Directory.GetFiles() and Directory.GetDirectories exist and that they accept a filter, but i need one method that does this recursively and flexibly and returns a set of absolute paths.
One option is to use a library like Glob library which unlike .net file globbing does folders with wild cards.
var folder = #"Your path goes here";
string[] folderPatterns = { "**/bin", "**/obj" };
string[] filePatterns = { "**/*cs"};
var results = new List<string>();
foreach (var pattern in folderPatterns)
{
results.AddRange(Glob.Directories(folder, pattern).ToArray());
}
foreach (var pattern in filePatterns)
{
results.AddRange(Glob.Files(folder, pattern).ToArray());
}
I have a List<string> containing file paths. How can I check if there is a particular file in my list e.g. file.txt? Preferably a method returning not a bool value but list element or element's index.
I've read the List<T> documentation but it only confused me as I'm a beginning programmer and my English isn't very good.
Use Where to get a list of values:
var found = fileList.
Where((f)=>System.IO.Path.GetFileName(f)
.Equals(SEARCH_VALUE,
StringComparison.InvariantCultureIgnoreCase));
or use FirstOrDefault for single element or null in case it's not found
If your list contains the full path (like c:\windows\system.ini") I would use System.IO.Path.GetFileName and also keep in mind to search case intenstive
var result = from f in files
where Path.GetFileName(f).Equals("file.txt",
StringComparison.InvariantCultureIgnoreCase)
select f;
bool found = result.Any();
The IndexOf method is what you need, if you want to find the path that exactly watches what you are looking for.
However, if you what to find paths in your list that end with a certain file name, you can do,
var matches = paths.Select((path, i) => new { Path = path, Index = i })
.Where(item => Path.GetFileName(item.Path).Equals(
"file.txt",
StringComparison.OrdinalIgnoreCase));
However, note that matches will be a sequence of 0 or more matches. So, you can do,
if (matches.Any())
{
// I found something.
foreach (var match in matches)
{
var matchIndex = match.Index;
var matchPath = match.Path;
}
}
else
{
// Oops, no matches.
}
or, if you only want the first.
var firstMatchPath = matches.First().Path;
would do.
If you just want the first value if there is one then you can do this.
var value = mylist.FirstOrDefault(x=>x.EndsWith("file.txt"));
or if you want to do something with each matching string.
foreach (string value in mylist.Where(x=>x.EndsWith("file.txt")) )
{
// Do whatever you intend with value.
}
or if you want a list of the indices of the values, then you could try this.
var indexValues = new List<int>();
foreach (string value in mylist.Where(x=>x.EndsWith("file.txt")) )
{
indexValues.Add(mylist.IndexOf(value));
}
Use LINQ (assuming you havethe paths as strings):
var found = from f in fileList where f.equals("file.txt") select f;
Considering that you have path and file name is located at the end of path:
//List of elements
List<string> foundElements = myInitialList.Where(s => s.EndsWith("file.txt")).ToList();
//List of Indexes (base on lift of elements here above)
List<int> indexList = new List<int>();
foundElements.ForEach(f => indexList.Add(myInitialList.IndexOf(f)));
It's not clear from your question, but it seems that the list will contain file paths, but what you are looking for is a filename.
The following code will give you the index of the first occurrence of a file called "file.txt" in the list of path names, or -1 if it isn't in the list.
Note how this uses Path.GetFileName(). This is so that it will match "c:\dir1\dir2\file.txt" and not "c:\dir1\dir2\wrongfile.txt".
int index = filenames.FindIndex(filename => Path.GetFileName(filename).Equals("file.txt", StringComparison.OrdinalIgnoreCase));
However, if you are searching for an entire path then you can do it like this:
int index = filenames.FindIndex(filename => filename.Equals("file.txt", StringComparison.OrdinalIgnoreCase));
Note how we do a comparison of the entire filename rather than using "EndsWith".
If the filenames are already all lowercase and you are comparing entire paths then you can do a simpler search:
int index = filenames.IndexOf("file.txt");
Or if they are all uppercase you'd have to do:
int index = filenames.IndexOf("FILE.TXT");
I'm trying to list out all files under a given directory by taking sub directories as well into account.I'm using yield so that I could club this with Take where I call this (note that I'm using .NET 3.5).
Below is my code:
IEnumerable<string> Search(string sDir)
{
foreach (var file in Directory.GetFiles(sDir))
{
yield return file;
}
foreach (var directory in Directory.GetDirectories(sDir))
{
Search(directory);
}
}
I don't know what is going wrong here, but it only returns one file (which is the one under the root directory, and there is only one there as well). Can you please help?
You need to yield the results of the recursive search, otherwise you're just throwing its results away:
IEnumerable<string> Search(string sDir)
{
foreach (var file in Directory.GetFiles(sDir))
{
yield return file;
}
foreach (var directory in Directory.GetDirectories(sDir))
{
foreach(var file in Search(directory))
yield return file;
}
}
Note that if your intent is to simply get a flat list of every file, consider using Directory.GetFiles instead with the option to search all subdirectories. If your intent is to leverage LINQ (or other methods) to apply searching criteria or a limit to the total number of files retrieved, then this is a decent way to go as you'll read directories one at a time and stop once you've fulfilled your criterion.
I'm trying to iterate through all the files on a certain level in the folder hierachy, more specifically, in all the sub-sub folders. Before I do actual operations on the files, I also want to count all the files to be able to show a progress bar. This means the iterating method must be called 2 times. This is the relevant code, I'm using now:
Iterate(bool count)
{
foreach (string dir in Directory.GetDirectories(root))
foreach (string subdir in Directory.GetDirectories(dir))
foreach (string file in Directory.GetFiles(subdir))
{
if (count) progressBar.Maximum++;
else
{
//do operations
}
}
}
I'm wondering if there's a better way of doing this. Surely there must be a better way than adding a foreach for every folder level..?
It'd be easier to me to use LINQ here.
var files =
(from dir in Directory.GetDirectories(root)
from subdir in Directory.GetDirectories(dir)
from f in Directory.GetFiles(subdir)
select f).ToList();
var fileCount = files.Length;
foreach (var f in files) {
...
}
Before your GetFiles foreach, try this:
string [] fileEntries = Directory.GetFiles(subdir);
int intFileCount = fileEntries.length;
Or it can replace it, if the loop only serves to count the files.
The documentation of Directory.GetFiles shows how to recursively iterate a directory tree
you could download the fluent thing I wrote for System.IO (see here: http://blog.staticvoid.co.nz/2011/11/staticvoid-io-extentions-nuget.html) and then use this LINQ statement
var files = from d in di.Directories()
from dir in d.Directories()
from f in dir.Files()
select f;
write this instead your code
string [] files = Directory.GetFile("yourDirectory","*.*",SearchOptions.AllDirectories);
this will return all files in sub-directories instead of using recursion
I have a set of subfolders 3 levels deep over with over 20k files in c:\MyData.
There is an almost identical set of subfolders on my E drive at e:\projects\massdata
I want to check in C and anything that already exists in E (same folder name, same file name, same size), I want to delete from C.
What is my best way of traversing this folder structure?
how about using the join operator. joining on filename like this
public void cleanUp()
{
var cFiles = Directory.GetFiles(#"c:\MyData","*.*",SearchOption.AllDirectories);
var fFiles = Directory.GetFiles(#"e:\projects\massdata","*.*",SearchOption.AllDirectories);
Func<string, string, Tuple<string, long>> keySelector = (path, root) =>
new Tuple<string, long>(path.Replace(root, ""), new FileInfo(path).Length);
foreach (var file in cFiles.Join(fFiles, f => keySelector(f,#"e:\projects\massdata"), c => keySelector(c,#"c:\MyData"), (c, f) => c))
{
File.Delete(file);
}
}
Second Edit after update:
The key selector should now meet your requirement. If I've misunderstood them. It sure be rather easy so see what you need to change. If not drop a comment :)
Recursively go thru all files in each directory.
Create a string describing the relative path,file size, etc. of the files in E in a hashMap. Then just check if a specific files relative path exists, when going thru C, and delete it if so.
The string could for instance be [FILENAME]##[FILESIZE]##[LASTEDITER].
Here is one way to search recursively in C#:
http://support.microsoft.com/kb/303974