C # Удалить все пустые подкаталоги - PullRequest
40 голосов
/ 11 мая 2010

У меня есть задача очистить большое количество каталогов. Я хочу начать с каталога и удалить все подкаталоги (независимо от их глубины), которые не содержат файлов (файлы никогда не будут удалены, только каталоги). Тогда начальный каталог будет удален, если он не содержит файлов или подкаталогов. Я надеялся, что кто-то может указать мне на какой-то существующий код для этого, вместо того, чтобы заново изобретать колесо. Я буду делать это с помощью C #.

Ответы [ 8 ]

82 голосов
/ 11 мая 2010

Использование кода C #.

static void Main(string[] args)
{
    processDirectory(@"c:\temp");
}

private static void processDirectory(string startLocation)
{
    foreach (var directory in Directory.GetDirectories(startLocation))
    {
        processDirectory(directory);
        if (Directory.GetFiles(directory).Length == 0 && 
            Directory.GetDirectories(directory).Length == 0)
        {
            Directory.Delete(directory, false);
        }
    }
}
39 голосов
/ 11 мая 2010

Если вы можете настроить таргетинг на .NET 4.0, вы можете использовать новые методы класса Directory для перечисления каталогов, чтобы не платить штраф за производительность при перечислении каждого файла в каталоге, когда вы просто хотите узнать, есть ли хотя бы один.

Методы:

  • Directory.EnumerateDirectories
  • Directory.EnumerateFiles
  • Directory.EnumerateFileSystemEntries

Возможная реализация с использованием рекурсии:

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) { }
}

Вы также упоминаете, что дерево каталогов может быть очень глубоким, поэтому возможно, что вы получите некоторые исключения, если исследуемый путь слишком длинный.

7 голосов
/ 14 апреля 2014

Выполнение теста в C: \ Windows 1000 раз по трем упомянутым выше методам дало следующее:

GetFiles+GetDirectories:630ms
GetFileSystemEntries:295ms
EnumerateFileSystemEntries.Any:71ms

Запуск его в пустой папке дал это (снова 1000 раз):

GetFiles+GetDirectories:131ms
GetFileSystemEntries:66ms
EnumerateFileSystemEntries.Any:64ms

Таким образом, EnumerateFileSystemEntries, безусловно, является лучшим в целом, когда вы проверяете наличие пустых папок.

3 голосов
/ 14 марта 2012

Если вы используете 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();
}
3 голосов
/ 11 мая 2010

Отсюда Скрипт Powershell для удаления пустых каталогов :

$items = Get-ChildItem -Recurse

foreach($item in $items)
{
      if( $item.PSIsContainer )
      {
            $subitems = Get-ChildItem -Recurse -Path $item.FullName
            if($subitems -eq $null)
            {
                  "Remove item: " + $item.FullName
                  Remove-Item $item.FullName
            }
            $subitems = $null
      }
}

Примечание : используйте на свой страх и риск!

2 голосов
/ 30 июля 2015

Вот версия, использующая преимущества параллельного выполнения для ускорения выполнения в некоторых случаях:

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);
}

... и вот некоторые результаты моей машины для каталога, находящегося в общей папке в глобальной сети. Эта папка в настоящее время имеет только 16 подпапок и 2277 файлов.

Parallel: 1479
Single: 4724
Parallel: 1691
Single: 5603
Parallel: 1540
Single: 4959
Parallel: 1592
Single: 4792
Parallel: 1671
Single: 4849
Parallel: 1485
Single: 4389
1 голос
/ 16 июня 2014
    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);
        }
    }
}
0 голосов
/ 22 мая 2013
//Recursive scan of empty dirs. See example output bottom

string startDir = @"d:\root";

void Scan(string dir, bool stepBack)
    {
        //directory not empty
        if (Directory.GetFileSystemEntries(dir).Length > 0)
        {
            if (!stepBack)
            {
                foreach (string subdir in Directory.GetDirectories(dir))
                    Scan(subdir, false);
            } 
        }
        //directory empty so delete it.
        else
        {
            Directory.Delete(dir);
            string prevDir = dir.Substring(0, dir.LastIndexOf("\\"));
            if (startDir.Length <= prevDir.Length)
                Scan(prevDir, true);
        }
    }
//call like this
Scan(startDir, false);

/*EXAMPLE outputof d:\root with empty subfolders and one filled with files
   Scanning d:\root
   Scanning d:\root\folder1 (not empty)
   Scanning d:\root\folder1\folder1sub1 (not empty)
   Scanning d:\root\folder1\folder1sub1\folder2sub2 (deleted!)
   Scanning d:\root\folder1\folder1sub1 (deleted!)
   Scanning d:\root\folder1 (deleted)
   Scanning d:\root (not empty)
   Scanning d:\root\folder2 (not empty)
   Scanning d:\root\folder2\folder2sub1 (deleted)
   Scanning d:\root\folder2 (not empty)
   Scanning d:\root\folder2\notempty (not empty) */
...