Using the EnumerateDirectories how do you enumerate only 2 folder structures down. Example: If I start at C:\, how do I get the folders inside of C as well as one additional level down?
The only thing that Directory.GetDirectories() offers so far is the SearchOption of
SearchOption.AllDirectories || SearchOption.TopDirectoryOnly
This is what I have so far:
private static List<string> GetDirectories(string path, string searchPattern = "*")
{
try
{
return Directory.GetDirectories(path, searchPattern, SearchOption.TopDirectoryOnly).ToList();
}
catch (UnauthorizedAccessException)
{
return new List<string>();
}
}
You use a counter for the level and call the method recursively.
Untested code:
private static List<string> GetDirectories(string path, int level, string searchPattern = "*")
{
if (level == 0)
return Directory.GetDirectories(path, searchPattern, SearchOption.TopDirectoryOnly).ToList();
else
{
List<string> l = new List<string>();
foreach (string path2 in Directory.GetDirectories(path, searchPattern, SearchOption.TopDirectoryOnly))
l.AddRange(GetDirectories(path2, level - 1, searchPattern));
return l;
}
}
and call like this:
return GetDirectories(yourPath, 1, yourSearchPattern);
or you can change this to drill down, then go back up for the next etc. but still using a counter.
If you need the files (though the body of your question didn't mention that, only the title), just iterate over the result, getting the files in each of the folders you got.
You could do something like this, you'll just need to add some code if you're not authorized to access the directory.
Directory.GetDirectories(path, searchPattern).ToList().ForEach(
d =>
{
try
{
searchItems.Add(d);
searchItems.AddRange(Directory.GetDirectories(d, searchPattern, SearchOption.TopDirectoryOnly));
}
catch (UnauthorizedAccessException)
{
//do something when you are not authorized
}
});
Related
How can append f to a list each time, then write the list into a text file ? I tried with the following code but the text file is always empty?
static void DirSearch(string dir, string pattern)
{
try
{
List<String> filesList = new List<string>();
foreach (string f in Directory.GetFiles(dir, pattern))
//Console.WriteLine(f);
{
// Console.WriteLine(f); // // <- This works correctly
filesList.Add(f);
}
foreach (string d in Directory.GetDirectories(dir))
{
//Console.WriteLine(d);
DirSearch(d, pattern);
}
File.WriteAllLines("files.txt", filesList.ToArray());
}
catch (System.Exception ex)
{
//Console.WriteLine(ex.Message);
}
}
I suspect the problem is that on each invocation of DirSearch, you're overwriting the file output from previous invocations. If the final invocation has no files (only directories) you'll end up with an empty file.
Options:
Use Directory.GetFiles(dir, pattern, SearchOption.AllDirectories so you don't need to use recursion at all.
Use File.AppendAllLines instead of File.WriteAllLines
Build up the list entirely in memory (recursively) and only at the very end call File.WriteAllLines.
The latter approach would look something like this:
public static DirSearch(string dir, string pattern)
{
List<string> files = new List<string>();
DirSearchImpl(dir, pattern, files);
File.WriteAllLines("files.txt", files);
}
private static DirSearchImpl(string dir, string pattern, List<string> files)
{
// Simpler than your previous loop...
files.AddRange(Directory.GetFiles(dir, pattern));
foreach (var subdirectory in Directory.GetDirectories(dir))
{
DirSearchImpl(subdirectory, pattern, files);
}
}
I'd also suggest changing your exception handling - catching Exception is rarely a good idea, and I'm not sure you really want to keep going if things fail, do you?
I have this test path:
private static string dCrawler = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "TestLetters";
Is there a way to say:
foreach (item in dCrawler)
{
if (item.isFile)
{
// check file info date modified code
} else
{
foreach (fileinfo file in ...
}
}
so far I have only found ways to check a file in a directory. Is the only way to do it by having two separate loops one for files and one for folders?
You can use Directory.GetFiles(); that returns a string[] and use the string value to create your FileInfo. Like this
foreach (string n in Directory.GetFiles(dCrawler))
{
FileInfo b = new FileInfo(n);
}
To get directories, you can similarly use Directory.GetDirectories();
foreach (string n in Directory.GetDirectories(dCrawler))
{
DirectoryInfo b = new DirectoryInfo(n);
}
I have this line of code: (using LINQ)
//string folder <-- folder browser dialog.
listFiles = Directory.GetFiles(folder, "*.xml",
SearchOption.AllDirectories).Select(
fileName => Path.GetFullPath(fileName)).ToList();
But sometimes my program finds protected files, such as system files or even system folders that can't be opened.
How can I surpass this problem:
Only get file name of open/free files-folders.
You can't tell, you just have to catch the exception.
What if the file is free when doing the free check, but in use when processing?
That can be a problem. If it throws an exception when going through the directories, it stops.
If you want to ignore those directories and keep going, you have to write a recursive method to do it:
List<string> GetFiles(string folder, string filter)
{
List<string> files = new List<string>();
try
{
// get all of the files in this directory
files.AddRange(Directory.GetFiles(folder, filter));
// Now recursively visit the directories
foreach (var dir in Directory.GetDirectories(folder))
{
files.AddRange(GetFiles(dir, filter));
}
}
catch (UnauthorizedAccessException)
{
// problem accessing this directory.
// ignore it and move on.
}
return files;
}
A somewhat more memory efficient version would be:
private List<string> GetFiles(string folder, string filter)
{
var files = new List<string>();
// To create a recursive Action, you have to initialize it to null,
// and then reassign it. Otherwise the compiler complains that you're
// using an unassigned variable.
Action<string> getFilesInDir = null;
getFilesInDir = new Action<string>(dir =>
{
try
{
// get all the files in this directory
files.AddRange(Directory.GetFiles(dir, filter));
// and recursively visit the directories
foreach (var subdir in Directory.GetDirectories(dir))
{
getFilesInDir(subdir);
}
}
catch (UnauthorizedAccessException)
{
// ignore exception
}
});
getFilesInDir(folder);
return files;
}
you could use something like this, potentially you will have to tweak attribute check:
Directory.GetFiles(folder, "*.xml", SearchOption.AllDirectories)
.Select(fileName => new FileInfo(Path.GetFullPath(fileName)))
.Where(n => !n.Attributes.HasFlag(FileAttributes.System))
.Select(n => n.FullName)
.ToList();
I wrote a method that needed to find all files within a path, and I want to get all the files using recursion. Here's my current method:
public void doStart(DirectoryInfo dir, string filePattern)
{
try
{
foreach (FileInfo fileInfo in dir.GetFiles(filePattern))
{
if (fileFound != null)
{
fileFound(fileInfo);
}
}
}
catch (Exception)
{
}
try
{
foreach (DirectoryInfo dirInfo in dir.GetDirectories())
{
doStart(dirInfo, filePattern);
}
}
catch (Exception)
{
}
}
public void Start(string path, string filePattern)
{
doStart(new DirectoryInfo(path), filePattern);
}
Is there is better way to write this kind of recursion or is this good enough ?
Try something like this:
string[] filePaths = Directory.GetFiles(#dir, "*.filetype", SearchOption.AllDirectories);
This would recursively look through the directory, finding all files with a certain filetype ('.filetype') and returns a string array containing all found files.
Also, I'd recommend not to use empty catch blocks, as your application won't let you know if something went wrong. Either show a message box (or something similar) or log it to a database or something.
Further, what would your DoStart() method do if there is a subdirectory in a subdirectory? From what I'm seeing, I'd say it only searches on 1 sublevel.
Don't swallow all exceptions. If you need to ignore specific exceptions, catch those but let others bubble up
(style) Methods should be PascalCased (e.g. DoStart and `FileFound'
(style) Create an OnFileFound method instead of calling FileFound directly (I assume fileFound is an event handler?)
Other than that it looks fine to me.
Here is an example of true recursion. This will search until there are no more sub-directories to find, unlike Directory.GetFiles SearchOption.AllDirectories. You can modify this to add search filters as a parameter.
public IEnumerable<string> GetFilesRecursive(string ParentDirectory)
{
string[] subDirectories = Directory.GetDirectories(ParentDirectory);
foreach (string file in Directory.GetFiles(ParentDirectory))
{
yield return file;
}
foreach (string subDirectory in subDirectories)
{
foreach (string file in GetFilesRecursive(subDirectory))
{
yield return file;
}
}
}
Listing all files in a drive other than my system drive throws an UnauthorizedAccessException.
How can I solve this problem?
Is there a way to grant my application the access it needs?
My code:
Directory.GetFiles("S:\\", ...)
Here's a class that will work:
public static class FileDirectorySearcher
{
public static IEnumerable<string> Search(string searchPath, string searchPattern)
{
IEnumerable<string> files = GetFileSystemEntries(searchPath, searchPattern);
foreach (string file in files)
{
yield return file;
}
IEnumerable<string> directories = GetDirectories(searchPath);
foreach (string directory in directories)
{
files = Search(directory, searchPattern);
foreach (string file in files)
{
yield return file;
}
}
}
private static IEnumerable<string> GetDirectories(string directory)
{
IEnumerable<string> subDirectories = null;
try
{
subDirectories = Directory.EnumerateDirectories(directory, "*.*", SearchOption.TopDirectoryOnly);
}
catch (UnauthorizedAccessException)
{
}
if (subDirectories != null)
{
foreach (string subDirectory in subDirectories)
{
yield return subDirectory;
}
}
}
private static IEnumerable<string> GetFileSystemEntries(string directory, string searchPattern)
{
IEnumerable<string> files = null;
try
{
files = Directory.EnumerateFileSystemEntries(directory, searchPattern, SearchOption.TopDirectoryOnly);
}
catch (UnauthorizedAccessException)
{
}
if (files != null)
{
foreach (string file in files)
{
yield return file;
}
}
}
}
You can the use it like this:
IEnumerable<string> filesOrDirectories = FileDirectorySearcher.Search(#"C:\", "*.txt");
foreach (string fileOrDirectory in filesOrDirectories)
{
// Do something here.
}
It's recursive, but the use of yield gives it a low memory footprint (under 10KB in my testing). If you want only files that match the pattern and not directories as well just replace EnumerateFileSystemEntries with EnumerateFiles.
Are you allowed to access the drive? Can the program access the drive when it's not run from Visual Studio? Are restrictive permissions defined in the project's Security page ("Security Page, Project Designer")?
In .net core you can do something like this below. It can search for all subdirectories recursively with good performance and ignoring paths without access.
I also tried other methods found in
How to quickly check if folder is empty (.NET)? and
Is there a faster way than this to find all the files in a directory and all sub directories? and
https://www.codeproject.com/Articles/1383832/System-IO-Directory-Alternative-using-WinAPI
public static IEnumerable<string> ListFiles(string baseDir)
{
EnumerationOptions opt = new EnumerationOptions();
opt.RecurseSubdirectories = true;
opt.ReturnSpecialDirectories = false;
//opt.AttributesToSkip = FileAttributes.Hidden | FileAttributes.System;
opt.AttributesToSkip = 0;
opt.IgnoreInaccessible = true;
var tmp = Directory.EnumerateFileSystemEntries(baseDir, "*", opt);
return tmp;
}
I solved the problem. Not really but at least the source.
It was the SearchOption.AllDirectories option that caused the exception.
But when I just list the immediate files using Directories.GetFiles, it works.
This is good enough for me.
Any way to solve the recursive listing problem?