C# 删除所有空的子目录

C# 删除所有空的子目录,c#,.net,subdirectory,C#,.net,Subdirectory,我的任务是清理大量目录。我想从一个目录开始,删除任何不包含任何文件(文件永远不会被删除,只有目录)的子目录(无论有多深)。如果起始目录不包含任何文件或子目录,则会将其删除。我希望有人能为我指出一些现有的代码,而不是重新发明轮子。我将使用C#完成此操作。从这里开始: 注意:自担风险使用 使用C#代码 如果您可以以.NET 4.0为目标,那么您可以使用目录类上的新方法枚举目录,以便在您只想知道目录中是否至少有一个文件时,在列出目录中的每个文件时不必支付性能损失 这些方法是: 目录。枚举目录 目录.

我的任务是清理大量目录。我想从一个目录开始,删除任何不包含任何文件(文件永远不会被删除,只有目录)的子目录(无论有多深)。如果起始目录不包含任何文件或子目录,则会将其删除。我希望有人能为我指出一些现有的代码,而不是重新发明轮子。我将使用C#完成此操作。

从这里开始:

注意:自担风险使用

使用C#代码


如果您可以以.NET 4.0为目标,那么您可以使用
目录
类上的新方法枚举目录,以便在您只想知道目录中是否至少有一个文件时,在列出目录中的每个文件时不必支付性能损失

这些方法是:

  • 目录。枚举目录
  • 目录.枚举文件
  • 目录。枚举文件系统
使用递归的可能实现:

static void Main(string[] args)
{
    DeleteEmptyDirs("Start");
}

static void DeleteEmptyDirs(string dir)
{
    if (String.IsNullOrEmpty(dir))
        throw new ArgumentException(
            "Starting directory is a null reference or an empty string", 
            "dir");

    try
    {
        foreach (var d in Directory.EnumerateDirectories(dir))
        {
            DeleteEmptyDirs(d);
        }

        var entries = Directory.EnumerateFileSystemEntries(dir);

        if (!entries.Any())
        {
            try
            {
                Directory.Delete(dir);
            }
            catch (UnauthorizedAccessException) { }
            catch (DirectoryNotFoundException) { }
        }
    }
    catch (UnauthorizedAccessException) { }
}

您还提到,目录树可能非常深,因此如果要探测的路径太长,则可能会出现一些异常。

如果依赖于
DirectoryInfo.Delete
仅删除空目录,则可以编写简洁的扩展方法

public static void DeleteEmptyDirs(this DirectoryInfo dir)
{
    foreach (DirectoryInfo d in dir.GetDirectories())
        d.DeleteEmptyDirs();

    try { dir.Delete(); }
    catch (IOException) {}
    catch (UnauthorizedAccessException) {}
}
用法:

static void Main()
{
    new DirectoryInfo(@"C:\temp").DeleteEmptyDirs();
}
//递归扫描空目录。请参见示例输出底部
字符串startDir=@“d:\root”;
无效扫描(字符串方向,布尔步进)
{
//目录不为空
if(Directory.GetFileSystemEntries(dir).Length>0)
{
如果(!后退)
{
foreach(Directory.GetDirectories(dir)中的字符串subdir)
扫描(细分,假);
} 
}
//目录为空,请删除它。
其他的
{
目录.删除(dir);
字符串prevDir=dir.Substring(0,dir.LastIndexOf(“\\”);

如果(startDir.Length在C:\Windows上对迄今为止提到的3种方法进行1000次测试,就会得出以下结果:

GetFiles+GetDirectories:630ms
GetFileSystemEntries:295ms
EnumerateFileSystemEntries.Any:71ms
在空文件夹上运行它会产生以下结果(再次运行1000次):

因此,在检查空文件夹时,EnumerateFileSystemEntries是迄今为止最好的整体。

私有静态void deleteEmptySubFolders(string ffd,bool deleteIFileSizezero=false)
    private static void deleteEmptySubFolders(string ffd, bool deleteIfFileSizeZero=false)
{
    DirectoryInfo di = new DirectoryInfo(ffd);
    foreach (DirectoryInfo diSon in di.GetDirectories("*", SearchOption.TopDirectoryOnly))
    {
        FileInfo[] fis = diSon.GetFiles("*.*", SearchOption.AllDirectories);
        if (fis == null || fis.Length < 1)
        {
            diSon.Delete(true);
        }
        else
        {
            if (deleteIfFileSizeZero)
            {
                long total = 0;
                foreach (FileInfo fi in fis)
                {
                    total = total + fi.Length;
                    if (total > 0)
                    {
                        break;
                    }
                }

                if (total == 0)
                {
                    diSon.Delete(true);
                    continue;
                }
            }

            deleteEmptySubFolders(diSon.FullName, deleteIfFileSizeZero);
        }
    }
}
{ DirectoryInfo di=新的DirectoryInfo(ffd); foreach(di.GetDirectories(“*”,SearchOption.TopDirectoryOnly中的DirectoryInfo diSon)) { FileInfo[]fis=diSon.GetFiles(“**”,SearchOption.AllDirectories); 如果(fis==null | | fis.Length<1) { diSon.Delete(true); } 其他的 { 如果(删除IffileSizezero) { 长总计=0; foreach(fis中的文件信息fi) { 总计=总计+fi.长度; 如果(总数>0) { 打破 } } 如果(总计==0) { diSon.Delete(true); 继续; } } deleteEmptySubFolders(diSon.FullName,deleteIfFileSizeZero); } } }
以下是一个版本,它利用并行执行在某些情况下更快地完成:

以下是单线程模式下的相同代码:

public static void DeleteEmptySubdirectoriesSingleThread(string parentDirectory){
  foreach(string directory in System.IO.Directory.GetDirectories(parentDirectory)){
    DeleteEmptySubdirectories(directory);
    if(!System.IO.Directory.EnumerateFileSystemEntries(directory).Any()) System.IO.Directory.Delete(directory, false);
  }
}
…下面是一些示例代码,可用于在场景中测试结果:

var stopWatch = new System.Diagnostics.Stopwatch();
for(int i = 0; i < 100; i++) {
  stopWatch.Restart();
  DeleteEmptySubdirectories(rootPath);
  stopWatch.Stop();
  StatusOutputStream.WriteLine("Parallel: "+stopWatch.ElapsedMilliseconds);
  stopWatch.Restart();
  DeleteEmptySubdirectoriesSingleThread(rootPath);
  stopWatch.Stop();
  StatusOutputStream.WriteLine("Single: "+stopWatch.ElapsedMilliseconds);
}


使用powershell怎么样…?powershell不是一个选项,抱歉。谢谢。主题是关于csharp@shtse8:完成任务就是使用任何简单的工具。谢谢,但不幸的是我们不使用.Net 4.0。我希望我们可以,因为我有大约20000个文件夹要处理。回答不错。而不是
if(String.IsNullOrEmpty)(entries.FirstOrDefault())
,如果(!entries.Any()),也可以使用
,这比IMHO更干净。@Danko Durbić完全同意你的观点,我没有注意到没有参数的重载,我已经在问自己为什么
可枚举的
没有什么东西可以快速检查空的
IEnumerable
。谢谢,我更新了答案。这是大目录的答案,因为对于大小适中的目录,目录是一种缓慢的方式。我想说的是,如果您没有.net 4.0,那么您应该使用来自此线程的API调用编写
if
语句的更快方式可以是
if(Directory.getfilesystemments(Directory.Length==0)
(!Directory.enumeratefilesystemments(Directory.Any)())更好此方法对我有效,但经过进一步思考,我不得不再次考虑以下事实:您正在使用第三行调用方法内部的方法名称:
processDirectory(directory)
!Huh???@user1985189:这称为递归函数。请参阅:Get*调用在返回前枚举所有条目,枚举调用生成项,因此检查任何项会使其提前退出(执行较少操作)比收集所有条目更有效-这可能会对包含大量项目的目录产生巨大影响此算法将删除给定开始目录中的所有空目录。删除目录时,将重新扫描上一个目录,因为上一个目录可能现在为空。这是不需要的。您应该k关于递归的好处,可能会慢慢地通过已经提供的答案进行调试。是的,但这其中有什么不是递归的?我有点太快了,所以我做了一个小的更正。请参阅关于此代码的输出示例。我认为这是有效的。如果您将
foreach
放在
If
语句上方,您将ldn不需要
stepBack
参数,该参数将使代码更易于阅读(结束您的代码将得到与已给出答案相同的结果)。此外,您还应该使用
目录。枚举…()
,而不是

GetFiles+GetDirectories:131ms
GetFileSystemEntries:66ms
EnumerateFileSystemEntries.Any:64ms
    private static void deleteEmptySubFolders(string ffd, bool deleteIfFileSizeZero=false)
{
    DirectoryInfo di = new DirectoryInfo(ffd);
    foreach (DirectoryInfo diSon in di.GetDirectories("*", SearchOption.TopDirectoryOnly))
    {
        FileInfo[] fis = diSon.GetFiles("*.*", SearchOption.AllDirectories);
        if (fis == null || fis.Length < 1)
        {
            diSon.Delete(true);
        }
        else
        {
            if (deleteIfFileSizeZero)
            {
                long total = 0;
                foreach (FileInfo fi in fis)
                {
                    total = total + fi.Length;
                    if (total > 0)
                    {
                        break;
                    }
                }

                if (total == 0)
                {
                    diSon.Delete(true);
                    continue;
                }
            }

            deleteEmptySubFolders(diSon.FullName, deleteIfFileSizeZero);
        }
    }
}
public static void DeleteEmptySubdirectories(string parentDirectory){
  System.Threading.Tasks.Parallel.ForEach(System.IO.Directory.GetDirectories(parentDirectory), directory => {
    DeleteEmptySubdirectories(directory);
    if(!System.IO.Directory.EnumerateFileSystemEntries(directory).Any()) System.IO.Directory.Delete(directory, false);
  });   
}
public static void DeleteEmptySubdirectoriesSingleThread(string parentDirectory){
  foreach(string directory in System.IO.Directory.GetDirectories(parentDirectory)){
    DeleteEmptySubdirectories(directory);
    if(!System.IO.Directory.EnumerateFileSystemEntries(directory).Any()) System.IO.Directory.Delete(directory, false);
  }
}
var stopWatch = new System.Diagnostics.Stopwatch();
for(int i = 0; i < 100; i++) {
  stopWatch.Restart();
  DeleteEmptySubdirectories(rootPath);
  stopWatch.Stop();
  StatusOutputStream.WriteLine("Parallel: "+stopWatch.ElapsedMilliseconds);
  stopWatch.Restart();
  DeleteEmptySubdirectoriesSingleThread(rootPath);
  stopWatch.Stop();
  StatusOutputStream.WriteLine("Single: "+stopWatch.ElapsedMilliseconds);
}
Parallel: 1479
Single: 4724
Parallel: 1691
Single: 5603
Parallel: 1540
Single: 4959
Parallel: 1592
Single: 4792
Parallel: 1671
Single: 4849
Parallel: 1485
Single: 4389
    foreach (var folder in Directory.GetDirectories(myDir, "*", System.IO.SearchOption.AllDirectories))
    {
        {
            try
            {
                if (Directory.GetFiles(folder, "*", System.IO.SearchOption.AllDirectories).Length == 0)
                    Directory.Delete(folder, true);
            }
            catch { }
        }
    }