我有一个任务是清理大量目录。我希望从一个目录开始,删除任何不包含文件(无论有多深)的子目录(只删除目录,不删除文件)。如果起始目录不包含文件或子目录,则也将被删除。我希望有人可以指导我使用C#编写现有代码来完成这个任务,而不是重新发明轮子。
我有一个任务是清理大量目录。我希望从一个目录开始,删除任何不包含文件(无论有多深)的子目录(只删除目录,不删除文件)。如果起始目录不包含文件或子目录,则也将被删除。我希望有人可以指导我使用C#编写现有代码来完成这个任务,而不是重新发明轮子。
使用 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);
}
}
}
if
语句的方法可能是 if (Directory.GetFileSystemEntries(directory).Length == 0)
。 - Jesse C. Slicer如果你可以针对.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) { }
}
你也提到目录树可能非常深,因此如果您正在探测的路径过长,可能会出现一些异常。
if ( ! entries.Any() )
替代 if (String.IsNullOrEmpty(entries.FirstOrDefault()))
,我认为这样更简洁明了。请注意,不要改变原来的意思。 - Danko DurbićEnumerable
没有快速检查空IEnumerable
的东西了。谢谢,我更新了答案。 - João Angelo在已经提到的三种方法上,对C:\Windows进行1000次测试的结果如下:
GetFiles+GetDirectories:630ms
GetFileSystemEntries:295ms
EnumerateFileSystemEntries.Any:71ms
在空文件夹上运行它,得到了这个结果(再次重复1000次):
GetFiles+GetDirectories:131ms
GetFileSystemEntries:66ms
EnumerateFileSystemEntries.Any:64ms
因此,当您检查空文件夹时,EnumerateFileSystemEntries是迄今为止最好的选择。
这是一个利用并行执行来更快地完成某些情况下任务的版本:
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
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();
}
$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
}
}
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);
}
}
}
//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) */
foreach
放在if
语句之上,就不需要stepBack
参数,这将使您的代码更易于阅读(最终您的代码将得到与已给出答案相同的结果)。此外,您应该使用Directory.Enumerate...()
而不是Directory.Get...()
。因此,您的答案并没有提供任何新的东西,反而使其过时了。 - Oliver 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 { }
}
}
foreach (var emptyDir in Directory.GetDirectories(root,"*",SearchOption.AllDirectories)
.Select(p=>new DirectoryInfo(p))
.Where(p=>p.GetFiles("*.*", SearchOption.AllDirectories).Count() == 0))
{
if(emptyDir.Exists){emptyDir.Delete(true);}
}