Reputation: 12140
I think I formerly knew how to do this, but it seems that I have forgotten.
I have a recursive algorithm to remove all empty directories in a directory tree:
static bool DeleteDirectoriesRecursive(string path)
{
var remove = true;
foreach (var dir in System.IO.Directory.GetDirectories(path))
{
remove &= DeleteDirectoriesRecursive(dir);
}
if (remove &= (System.IO.Directory.GetFiles(path).Length == 0))
System.IO.Directory.Delete(path);
return remove;
}
I'm trying to eliminate the recursion from this algorithm, not so much to "fix" the algorithm (i.e., the similar question doesn't use the remove
variable, but I would like to keep it).
I have started a new function, using the Stack<>
class, but I can't think of a good way to return to the base path and take the actions that the sub-directories have determined. I guess unraveling non-tail recursion takes a bit more effort.
Upvotes: 2
Views: 1398
Reputation: 22220
Since Gabe mentioned the possibility of a StackOverflowException when using recursion I was inspired to make this work without it. I used the code here as a starting point. And here's what I came up with...
public static bool DeleteDirectories(string root)
{
bool removed = false;
var dirs = new Stack<string>();
var emptyDirStack = new Stack<string>();
var emptyDirs = new Dictionary<string, int>();
if (!System.IO.Directory.Exists(root))
{
throw new ArgumentException();
}
dirs.Push(root);
while (dirs.Count > 0)
{
string currentDir = dirs.Pop();
string[] subDirs;
try
{
subDirs = System.IO.Directory.GetDirectories(currentDir);
}
catch (UnauthorizedAccessException e)
{
Console.WriteLine(e.Message);
continue;
}
catch (System.IO.DirectoryNotFoundException e)
{
Console.WriteLine(e.Message);
continue;
}
if (Directory.GetFiles(currentDir).Length == 0)
{
emptyDirStack.Push(currentDir);
emptyDirs.Add(currentDir, subDirs.Length); // add directory path and number of sub directories
}
// Push the subdirectories onto the stack for traversal.
foreach (string str in subDirs)
dirs.Push(str);
}
while (emptyDirStack.Count > 0)
{
string currentDir = emptyDirStack.Pop();
if (emptyDirs[currentDir] == 0)
{
string parentDir = Directory.GetParent(currentDir).FullName;
Console.WriteLine(currentDir); // comment this line
//Directory.Delete(currentDir); // uncomment this line to delete
if (emptyDirs.ContainsKey(parentDir))
{
emptyDirs[parentDir]--; // decrement number of subdirectories of parent
}
removed = true;
}
}
return removed;
}
A few notes:
emptyDirs
Dictionary, along with the number of subdirectories it contains.emptyDirs
Dictionary in reverse order (I used Linq to accomplish this).I imagine there are more efficient methods but this isn't too bad, and it seems to work in my testing.
UPDATE : I got rid of the reversed dictionary in favor of a 2nd stack. I still use the dictionary for fast lookups, however.
Upvotes: 1
Reputation: 224913
The sans-recursion version is much more difficult and not much more efficient, so I would just recommend keeping your recursion. Also, here's a version that's a little cleaner:
static bool DeleteDirectoriesRecursive(DirectoryInfo d)
{
bool remove = true;
foreach (DirectoryInfo c in d.GetDirectories())
{
remove &= DeleteDirectoriesRecursive(c);
}
if (remove && d.GetFiles().Length == 0) {
d.Delete();
return true;
}
return false;
}
Upvotes: 1