我有这个删除空文件夹的递归方法:
private void DeleteEmpty(DirectoryInfo directory)
{
foreach (var d in directory.GetDirectories())
{
DeleteEmpty(d);
}
if (directory.GetFileSystemInfos().Length == 0)
{
try
{
directory.Delete();
}
catch (Exception)
{
// Already gone, no permission, not empty, et cetera
}
}
}
如何重构此方法以使其不是递归的?
答案 0 :(得分:3)
标准重构是存储您在LIFO(即堆栈)或FIFO队列中传递给函数的数据。请注意,这不会改变渐近空间的使用;你正在使用自己的数据结构而不是调用堆栈。
如果您可以定义“下一个兄弟”功能,则可以访问具有附加空间的节点。这是因为目录图(无文件)由于父指针而基本上是无向的。伪代码:
nextBranchingSibling(sibling):
while sibling exists
if sibling has children
return sibling
sibling = nextSibling(sibling)
return null
nextBranch(node):
if node is marked
unmark node
else
if nextBranchingSibling(firstChild(node)) exists
return nextBranchingSibling(firstChild(node))
if nextBranchingSibling(nextSibling(node)) exists
return nextBranchingSibling(nextSibling(node))
mark parent(node)
return parent(node)
prune(node):
while node exists:
tmpNode = node
node = nextBranch(node)
if count of tmpNode's children is 0
delete tmpNode
请注意,您实际上并未使用O(1)空间总数,因为目录结构本身就是O(n)。像DirectoryInfo.GetDirectories
这样的方法可以消除nextBranchingSibling
中对循环的需求。
答案 1 :(得分:3)
private static Queue<DirectoryInfo> directoryQueue = new Queue<DirectoryInfo>();
private void DeleteEmpty(DirectoryInfo directory)
{
directoryQueue.Enqueue(directory);
while (directoryQueue.Count > 0)
{
var current = directoryQueue.Dequeue();
foreach (var d in current.GetDirectories())
{
directoryQueue.Enqueue(d);
}
if (directory.GetFileSystemInfos().Length == 0)
{
try
{
directory.Delete();
}
catch (Exception)
{
// Already gone, no permission, not empty, et cetera
}
}
}
}
答案 2 :(得分:3)
试试这个:
private void DeleteEmpty(string path)
{
string[] directories = Directory.GetDirectories(
path, "*", SearchOption.AllDirectories);
// you should delete deeper directories first
// .OrderByDescending(
// dir => dir.Split(Path.DirectorySeparatorChar).Length)
// .ToArray();
foreach (string directory in directories)
{
DirectoryInfo info = new DirectoryInfo(directory);
if (info.GetFileSystemInfos().Length == 0)
{
info.Delete();
}
}
// If you wanna a LINQ-ish version
// directories.Where(dir =>
// new DirectoryInfo(dir).GetFileSystemInfos().Length == 0)
// .ToList().ForEach(dir => Directory.Delete(dir));
}
另一个性能步骤可能是:如果您尝试删除目录并且它包含文件,则应跳过所有父级别,因为它们也会失败。
答案 3 :(得分:1)
当堆栈不为空时,您可以使用本地堆栈和循环。
public void DeleteDirectories(DirectoryInfo directoryInfo, bool deleteFiles)
{
Stack<DirectoryInfo> directories = new Stack<DirectoryInfo>();
directories.Push(directoryInfo);
while (directories.Count > 0)
{
var current = directories.Peek();
foreach (var d in current.GetDirectories())
directories.Push(d);
if (current != directories.Peek())
continue;
if (deleteFiles)
foreach (var f in current.GetFiles())
{
f.Delete();
}
if (current.GetFiles().Length > 0 || current.GetDirectories().Length > 0)
throw new InvalidOperationException("The directory " + current.FullName + " was not empty and could not be deleted.");
current.Delete();
directories.Pop();
}
}
答案 4 :(得分:1)
我有同样的问题,我创建了一个很好的(imho)解决方案:在根目录中开始,我“递归地”获取子目录并将它们存储在ArrayList对象中。通过这种方式,我创建了一个列表,首先是较高级别的dirs,最后是较深层的嵌套目录。理想情况下,使用存储在LevelList对象级别中的索引将该数组划分为子数组。 这样做,我可以先查看更深层的目录,如果它们是空的则删除它们,然后逐级返回到根目录。
例如:
private void directoryCleanup(string root)
{
try
{
// Create directory "tree"
ArrayList dirs = new ArrayList();
// Beginning and ending indexes for each level
ArrayList levels = new ArrayList();
int start = 0;
dirs.Add(root);
while (start < dirs.Count)
{
ArrayList temp = new ArrayList();
for (int i = start; i < dirs.Count; i++)
{
DirectoryInfo dinfo = new DirectoryInfo((string)dirs[i]);
DirectoryInfo[] children = dinfo.GetDirectories();
for (int j = 0; j < children.Length; j++)
{
temp.Add(children[j].FullName);
}
Array.Clear(children, 0, children.Length);
children = null;
dinfo = null;
}
start = dirs.Count;
levels.Add(dirs.Count);
dirs.AddRange(temp);
temp.Clear();
temp = null;
}
levels.Reverse();
// Navigate the directory tree level by level, starting with the deepest one
for (int i = 0; i < levels.Count - 1; i++)
{
int end = (int)levels[i] - 1;
int begin = (int)levels[i + 1];
for (int j = end; j >= begin; j--)
{
string path = (string)dirs[j];
if (Directory.GetFileSystemEntries(path).Length == 0)
{
Directory.Delete(path);
}
}
}
levels.Clear();
levels = null;
dirs.Clear();
dirs = null;
}
catch (IOException ioex)
{
// Manage exception
return;
}
catch (Exception e)
{
// Manage exception
return;
}
}
答案 5 :(得分:0)
创建一个包含起始目录中所有目录的队列,然后当它不为空时,取下一个项目,检查目录是否为空,如果是,则删除它,如果没有将所有子目录添加到队列中。< / p>
我不知道C#,但是如果没有标准的队列类型,链接列表或可变数组类型的东西也可以正常工作。
伪代码;
directories = empty queue
until directories is not empty
next = directories.shift
if next is an empty folder
delete it
or else
add all the subdiretories to the queue