Check directory structure - c#

I'm trying to check whether a directory is in a format of
Ex:
This should be valid:
D:\TESTDIR\FOLDER1\FOLDER2\Somerandomfiles
This is not valid:
D:\TESTDIR\FOLDER1\Somerandomfiles
Basically I need to make sure that for every folder there should be a subfolder inside. note that a single folder may contain multiple subfolder. Then the subfolder will contain the files such as text files/pdf files etc.
//not sure how/where to place to check if folder has a subfolder
var _dir = Directory.GetDirectories(#"D:\TESTDIR");
foreach(var _folder1 in _dir)
{
// check if contains folder2? if not
// MessageBox.Show(folder1name);
var _folder2 = Directory.GetDirectories(_folder1);
foreach (var _path in _folder2)
{
// do something
}
}

You almost got it right, I just modified your code a little:
var dir = Directory.GetDirectories(#"D:\TESTDIR");
foreach(var folder1 in dir)
{
var folder2 = Directory.GetDirectories(folder1);
// If no directories are found in that subfolder, skip it.
// Basically it would work the same without if, as loop
// would not run even one iteration, but it's more readable.
if(folder2.Count == 0) continue;
foreach (var path in folder2)
{
var files = Directory.GetFiles(path);
// Same as above, check if there are any files.
if(files.Count == 0) break;
// Do some work
}
}

Here's how I would start this:
var query =
from dir in Directory.GetDirectories(#"D:\TESTDIR")
select new
{
dir,
valid =
Directory.GetDirectories(dir).Any()
&& Directory.GetDirectories(dir).All(child => Directory.GetFiles(child).Any())
};
That's going to check each of the directories in #"D:\TESTDIR" and ensure each has at least one subfolder and that each subfolder has at least one file.

Related

Directory.EnumerateDirectories : Only get back Directores with a special file in it [duplicate]

I wish to get list of all the folders/directories that has a particular file in it. How do I do this using C# code.
Eg: Consider I have 20 folders of which 7 of them have a file named "abc.txt". I wish to know all folders that has the file "abc.txt".
I know that we can do this by looking thru all the folders in the path and for each check if the File.Exists(filename); But I wish to know if there is any other way of doing the same rather than looping through all the folder (which may me little time consuming in the case when there are many folders).
Thanks
-Nayan
I would use the method EnumerateFiles of the Directory class with a search pattern and the SearchOption to include AllDirectories. This will return all files (full filename including directory) that match the pattern.
Using the Path class you get the directory of the file.
string rootDirectory = //your root directory;
var foundFiles = Directory.EnumerateFiles(rootDirectory , "abc.txt", SearchOption.AllDirectories);
foreach (var file in foundFiles){
Console.WriteLine(System.IO.Path.GetDirectoryName(file));
}
EnumerateFiles is only available since .NET Framework 4. If you are working with an older version of the .NET Framework then you could use GetFiles of the Directory class.
Update (see comment from PLB):
The code above will fail if the access to a directory in denied. In this case you will need to search each directory one after one to handle exceptions.
public static void SearchFilesRecursivAndPrintOut(string root, string pattern)
{
//Console.WriteLine(root);
try
{
var childDireactory = Directory.EnumerateDirectories(root);
var files = Directory.EnumerateFiles(root, pattern);
foreach (var file in files)
{
Console.WriteLine(System.IO.Path.GetDirectoryName(file));
}
foreach (var dir in childDireactory)
{
SearchRecursiv(dir, pattern);
}
}
catch (Exception exception)
{
Console.WriteLine(exception);
}
}
The following shows how to narrow down your search by specific criteria (i.e. include only DLLs that contain "Microsoft", "IBM" or "nHibernate" in its name).
var filez = Directory.EnumerateFiles(#"c:\MLBWRT", "*.dll", SearchOption.AllDirectories)
.Where(
s => s.ToLower().Contains("microsoft")
&& s.ToLower().Contains("ibm")
&& s.ToLower().Contains("nhibernate"));
string[] allFiles = filez.ToArray<string>();
for (int i = 0; i < allFiles.Length; i++) {
FileInfo fInfo = new FileInfo(allFiles[i]);
Console.WriteLine(fInfo.Name);
}

How to build a list of directories from args that may contain drive letters and/or wildcards

I want to pass a list of directories to my C# console application as arguments which may be relative paths based on the current working directory, or contain drive letters and/or wildcards. E.g.:
myApp.exe someDir another\dir dirs\with\wild*cards* ..\..\a\relative\dir C:\an\absolute\dir\with\wild*cards*
So far I've come up with this:
static void Main(string[] args)
{
List<string> directories = new List<string>();
foreach (string arg in args)
{
if (Directory.Exists(arg))
{
// 'arg' by itself is a valid directory, and must not contain any wildcards
// just add it to 'directories'
directories.Add(arg);
}
else
{
// 'arg' must either be a non-existant directory or invalid directory name,
// or else it contains wildcard(s). Find all matching directory names, starting
// at the current directory (assuming 'arg' might be a relative path), and add
// all matching directory names to 'directories'.
string[] dirs = Directory.GetDirectories(Directory.GetCurrentDirectory(), arg, SearchOption.TopDirectoryOnly);
directories.AddRange(dirs);
}
}
Console.WriteLine("Full list of directories specified by the command line args:");
foreach (string dir in directories)
{
Console.WriteLine(" " + dir);
}
// Now go do what I want to do for each of these directories...
}
This works great for someDir, another\dir, and dirs\with\wild*cards*, but won't work for ..\..\a\relative\dir or C:\an\abolute\dir\with\wild*cards*. For the relative dir, Directory.GetDirectories() throws a System.ArgumentException saying "Search pattern cannot contain '..' to move up directories and can be contained only internally in file/directory names, as in 'a..b'." For the drive-letter-based directory, it throws a System.ArgumentException saying "Second path fragment must not be a drive or UNC name."
How can I handle the ".." and drive letters? This has to be a solved problem, but I can't find examples of such for C#.
"Bonus question": My above code also doesn't handle a\path\with\wild*cards*\in\anything\other\than\the\top\directory. Any easy way to handle that too?
Couple of observations:
1) To check if path absolute or relative - you can use Path.IsRooted()
2) To resolve path with ".." to absolute path (be it relative or absolute) you can use:
path = new Uri(Path.Combine(Directory.GetCurrentDirectory(), path)).AbsolutePath;
Routing it though Uri expands those dots. Path.GetFullPath will fail in case of wildcards, but Uri will not.
3) To check if path contains wildcards you can just do path.Contains("*") || path.Contains("?") since both of those characters are not valid path chars, so cannot be present in a context other than being wildcards.
4) To resolve wildcard in absolute path (and match your "bonus" requirement) you need to find out first directory which does not contain wildcard. So you basically need to split path into to parts - before first wildcard and after first wildcard. For example:
C:\an\abolute\dir\with\wild*cards*
Path before wildcard is C:\an\abolute\dir\with, after (and including) wildcard: wild*cards*.
C:\an\abolu*e\dir\with\wild*cards*
Path before first wildcard: C:\an, after: abolu*e\dir\with\wild*cards*. There are different ways to do that of course, easiest I think is regex:
#"[\\/](?=[^\\/]*[\*?])"
It basically matches directory separator, but only if it is followed by 0 or more charactres which are NOT directory separators, then followed by wildcard symbol.
Combining this all together we have:
static IEnumerable<string> ResolveDirectories(string path) {
if (path.Contains("*") || path.Contains("?")) {
// we have a wildcard,
// resolve it to absolute path if necessary
if (!Path.IsPathRooted(path))
path = Path.Combine(Directory.GetCurrentDirectory(), path);
// resolve .. stuff if any
path = new Uri(Path.Combine(Directory.GetCurrentDirectory(), path)).AbsolutePath;
// split with regex above, only on first match (2 parts)
var parts = new Regex(#"[\\/](?=[^\\/]*[\*?])").Split(path, 2);
var searchRoot = parts[0];
var searchPatterns = parts[1].Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
foreach (var dir in ResolveWildcards(searchRoot, searchPatterns))
yield return dir;
}
else {
// this should work with "../.." type of paths too, will resolve relative to current directory
// so no processing is necessary
if (Directory.Exists(path)) {
yield return path;
}
else {
// invalid directory?
}
}
}
static IEnumerable<string> ResolveWildcards(string searchRoot, string [] searchPatterns) {
// if we have path C:\an\abolu*e\dir\with\wild*cards*
// search root is C:\an
// and patterns are: [abolu*e, dir, with, wild*cards*]
if (Directory.Exists(searchRoot)) {
// use next pattern to search in a search root
var next = searchPatterns[0];
// leave the rest for recursion
var rest = searchPatterns.Skip(1).ToArray();
foreach (var dir in Directory.EnumerateDirectories(searchRoot, next, SearchOption.AllDirectories)) {
// if nothing left (last pattern) - return it
if (rest.Length == 0)
yield return dir;
else {
// otherwise search with rest patterns in freshly found directory
foreach (var sub in ResolveWildcards(dir, rest))
yield return sub;
}
}
}
}
This is not properly tested, so take care.
'Evk' posted his/her answer a few minutes before I was about to post this, so I have not evaluated Evk's answer.
After finding this question+answer which might have worked fine but I didn't test it, I stumbled across Michael Ganss's Glob.cs NuGet package, and the results are very nice:
// Requires Glob.cs, (c) 2013 Michael Ganss, downloaded via the NuGet package manager:
// https://www.nuget.org/packages/Glob.cs
// (In Visual Studio, go to Tools->NuGet Package Manager->Package Manager Console.)
// PM> Install-Package Glob.cs -Version 1.3.0
using Glob;
namespace StackOverflow_cs
{
class Program
{
static void Main(string[] args)
{
List<string> directories = new List<string>();
foreach (string arg in args)
{
var dirs = Glob.Glob.Expand(arg, true, true);
foreach (var dir in dirs)
{
directories.Add(dir.FullName);
}
}
Console.WriteLine("Full list of directories specified by the command line args:");
foreach (string dir in directories)
{
Console.WriteLine(" " + dir);
}
// Now go do what I want to do for each of these directories......
}
}
}
But I don't know why I have to say "Glob.Glob.Expand()" instead of simply "Glob.Expand()". Anyway, it works beautifully.

Recursive function - Don't show directory if empty

I´m creating a web application that needs to list all the files and directories in a specific folder.
I´ve created the resurvice function to list these, but another specification is that if the directory has no files then the directory shouldn´t be shown.
Now the problem is, what if i have a directory with a submenu, with a submenu, with a submenu, with a submenu and only the last submenu has files in it. It still needs to show all the other directories.
I´ve no idea how to achieve this, any code or tips would be appreciated!
My current recursive function:
private List<FolderModel> GetFolderSubFolders(IFolderInfo folder)
{
var retval = new List<FolderModel>();
// Foreach subfolder in the given folder
foreach (var subFolder in FolderManager.Instance.GetFolders(folder))
{
// Create new foldermodel
var folderModel = new FolderModel
{
FolderName = subFolder.FolderName,
Bestanden = GetFolderBestanden(subFolder),
// Recall this function
SubFolders = GetFolderSubFolders(subFolder)
};
// Check if we have files and subfolders
if (folderModel.Bestanden.Any() && folderModel.SubFolders.Any())
{
folderModel.hasFilesAndFolders = true;
}
retval.Add(folderModel);
}
return retval;
}
Just let it find all directories and then filter for the ones which have files inside:
List<FolderModel> allFolders = GetFolderSubFolders(myFolder);
List<FolderModel> nonEmptyFolders = allFolders.Where(f => f.HasFiles).ToList();
I've not tried to run this but I think something like that should work:
private List<FolderModel> GetFolderSubFolders(IFolderInfo folder)
{
var retval = new List<FolderModel>();
// Foreach subfolder in the given folder
foreach (var subFolder in FolderManager.Instance.GetFolders(folder))
{
// Recall this function
List<FolderModel> subFolders = GetFolderSubFolders(subFolder);
// I am assuming that "Bestanden" is also a List<T> and contains
// the files of this folder
List<Something> bestanden = GetFolderBestanden(subFolder);
// Don't do anything if an empty list was returned
if(subFolders.Count > 0 || bestanden.Count > 0)
{
// Create new foldermodel
var folderModel = new FolderModel
{
FolderName = subFolder.FolderName,
Bestanden = bestanden,
SubFolders = subFolders
};
// Check if we have files and subfolders
if (folderModel.Bestanden.Any() && folderModel.SubFolders.Any())
{
folderModel.hasFilesAndFolders = true;
}
retval.Add(folderModel);
}
}
// If nothing was found this will return an empty list
return retval;
}
The idea is that you don't return anything if you don't find something at the last level. The above may not work as I've not run it but you may get a hint.

Delete Folder if it is Empty using C#

I am writing a tool that will allow me to go though a fairly large list of Directories and Sub-directories. I would like it to delete a folder if there it is empty. I can delete folders and sub folders that are empty with this code:
string dir = textBox1.Text;
string[] folders = System.IO.Directory.GetDirectories(dir, "*.*", System.IO.SearchOption.AllDirectories);
foreach (var directory in folders)
{
if (System.IO.Directory.GetFiles(directory).Length == 0 && System.IO.Directory.GetDirectories(directory).Length == 0)
{
System.IO.StreamWriter Dfile = new System.IO.StreamWriter(newpath, true);
System.IO.Directory.Delete(directory);
}
}
My question is how to have the code go though and check the folders after each delete because once it deletes a folder it could make the parent folder empty and should then should be deleted. Once the code does not find any folders or sub-folders that are empty it would exit.
Write a depth-first recursive function. As you complete each recursive call, check the current folder to see if it is empty. If it is, then delete it.
Something like this (pseudocode)
DeleteEmptyFolders(path)
{
foreach Folder f in Path
{
DeleteEmptyFolders(f);
if (f is empty)
{
Delete(f);
}
}
}
You can do this recursively like this (not tested):
void DeleteFolder(string folder) {
string[] folders = System.IO.Directory.GetDirectories(folder, "*.*", System.IO.SearchOption.AllDirectories);
foreach (var directory in folders)
{
DeleteFolder(directory);
}
//delete this folder if empty
}
Here's an idea (this isn't tested)
while ( true )
{
DirectoryInfo parent = Directory.GetParent(current.FullName);
if ( parent.GetFiles().Length == 0 && parent.GetDirectories().Length == 0 )
{
current = parent;
current.Delete();
}
else
{
break;
}
}
This walks up the tree of the current directory to delete any parent directories that are empty.
By the look of it your are trying to delete any folder which does not contain a file. I believe this would do the trick for you. I tested it with a small folder set that went 5 levels deep and a single file in a couple of locations. All folders which did not have files were deleted. All files were left intact. Small tweak to a snippet found... Thanks Matt Smith.
Cannot delete directory with Directory.Delete(path, true)
string[] dirs = System.IO.Directory.GetDirectories(Directory.GetCurrentDirectory(), "*.*",SearchOption.AllDirectories);
foreach (string d in dirs)
{
if (System.IO.Directory.Exists(d)) {
if (System.IO.Directory.GetFiles(d, "*.*", SearchOption.AllDirectories).Length == 0)
{
DeleteDirectory(d);
}
}
}
public static void DeleteDirectory(string target_dir)
{
string[] dirs = Directory.GetDirectories(target_dir);
foreach (string dir in dirs)
{
DeleteDirectory(dir);
}
Directory.Delete(target_dir, false);
}
this will delete all empty (sub)folders in a given directory
https://stackoverflow.com/a/16688997/2408998

how to read all the subdirectories in a given destination which contain Master File in it

I am having a problem .
My Problem is to read all the subdirectories in a given destination which contain Master file .
I can read subdirectories but i am creating a project which only read given directory which should contain Master file in the directory .
In the given directory a file called Master file should be there.
I want to write the code like if the given directory doesnot contain any Master file in it it should Jump to another Directory.
My source Directory is #"C:\test.
In #"C:\test" there are many folders and subfolders .
the test directory contains "C:\test\test1\test2\test3 . In this path test3 folder contains Master file test1 and test2 doesn't .
I want to write this code something like this,
MLMReader Reader = new MLMReader();
Reader.OpenDirectory(#"C:\test");
if (!File.Exists(test + "\\Master"))
{
//i want to loop the "C"\\" and if test1 does not contain
// Master File then jump to another directory test2, if
//test2 directory contain Master File then the work should
// continue after finishing go to test3
}
Is there any way to do .
Any suggestions for my problem.
I haven't tested, but I'm pretty sure the following will work:
string[] paths = Directory.GetFiles(dirPath, "MasterFile", SearchOption.AllDirectories);
Then you can just foreach over the resulting array, if you want to go through all MasterFiles. Or if you just care about the first result, then it's just paths[0] -- of course, means it does a lil bit extra work finding all matching paths. And you probably don't need a check for an empty array as an index out of bounds would indicate there's no MasterFile (unless you want to catch that and then rethrow as file not found exception or whatever).
Linq-to-FileSystem allows you to perform structured queries on your file system. See the following example:
var dir = new DirectoryInfo(#"C:\test");
// find all subdirectories of test that contains a file / folder called 'Master'
var dirs = dir.Elements()
.Where(d => d.Elements().Any(i => i.Name == "Master"))
this is my code where I program with directorys, hope it will help you.
using System;
using System.IO;
static void Print(string path, int? rec, int? tree, bool color, int? level = 0)
{
if ((rec != null && level > rec) || path == null) return;
if (rec == null) rec = 0;
string[] dir;
string[] fil;
try
{
dir = Directory.GetDirectories(path);
fil = Directory.GetFiles(path);
}
catch(Exception e)
{
Console.WriteLine(e.Message);
return;
}
foreach (string s in dir)
{
WriteSpace(level,tree);
Console.WriteLine(s);
Print(s, rec, tree, color, level + 1);
}
if (color)
{
ConsoleColor def = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Blue;
foreach (string s in fil) // vypis file
{
WriteSpace(level, tree);
Console.WriteLine(s);
}
Console.ForegroundColor = def;
}
else
{
foreach (string s in fil)
{
WriteSpace(level, tree);
Console.WriteLine(s);
}
}
}
private static void WriteSpace(int? level, int? tree)
{
for (int i = 0; i < level*tree; i++)
Console.Write(" ");
}
}
Here's a Recursive Example on how you can traverse a directory structure and look for a certain file, once it is found you can call the corresponding method.
static void Main()
{
TraverseDirectory(#"D:\TestDirectory");
}
static void DoSomethingWithMaster(string path)
{
Console.WriteLine("Found master at {0}", path);
}
static void TraverseDirectory(string directory)
{
var currentDirectory = new DirectoryInfo(directory);
foreach(var dir in currentDirectory.GetDirectories())
{
var currentPath = dir.FullName;
TraverseDirectory(currentPath);
var pathToMasterFile = Path.Combine(currentPath, "Master");
if (File.Exists(pathToMasterFile))
DoSomethingWithMaster(pathToMasterFile);
}
}
I have the following Folder Structure:
D:\TestDirectory\1
D:\TestDirectory\2
D:\TestDirectory\3
D:\TestDirectory\4
D:\TestDirectory\4\Master
D:\TestDirectory\5
And when running the above it prints: Found master at D:\TestDirectory\4
All you need for this is:
using System;
using System.IO;
You can also move TraverseDirectory(currentPath); to the end of the foreach-loop, where you put it depends on when you want to detect the file, do you want to go deep and then climb your way back and check for the Master-file on the way up, or do you want to check for the master file before you enter the next directory?
According to your question, you might want to swap them in my answer and if I understand you correctly, you might not even want to go to the next directory after finding one master-file?
foreach(var dir in currentDirectory.GetDirectories())
{
var currentPath = dir.FullName;
var pathToMasterFile = Path.Combine(currentPath, "Master");
if (File.Exists(pathToMasterFile))
DoSomethingWithMaster(pathToMasterFile);
TraverseDirectory(currentPath);
}
In this example, it does exactly what you are saying in your commented code inside your if. It will first check TestDirectory\1 for a Master file and then go further down the line. If you don't care about any other Master-files in one sub-directory once it is found, you can just nest TraverseDirectory(pathToMasterFile) inside an else-block.
Edit
You might want to use EnumerateDirectories, see MSDN for details.

Categories

Resources