如何快速检查文件夹是否为空(.NET) ?

我必须检查磁盘上的目录是否为空。它的意思是,它不包含任何文件夹/文件。我知道,有一个简单的方法。我们得到 FileSystemInfo 的数组并检查元素的计数是否等于零。差不多是这样:

public static bool CheckFolderEmpty(string path)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException("path");
}


var folder = new DirectoryInfo(path);
if (folder.Exists)
{
return folder.GetFileSystemInfos().Length == 0;
}


throw new DirectoryNotFoundException();
}

这种方法似乎可行。但是! !从性能的角度来看,这是非常非常糟糕的。GetFileSystemInfos ()是一种非常困难的方法。实际上,它枚举文件夹的所有文件系统对象,获取它们的所有属性,创建对象,填充类型化数组等。所有这一切只是为了简单地检查长度。很愚蠢,不是吗?

我只是分析了这样的代码,并确定,这种方法的约250个调用在约500ms 内执行。这是非常缓慢的,我相信,这是可能的,做得更快。

有什么建议吗?

159572 次浏览

您可以尝试 Directory.Exists(path)Directory.GetFiles(path)-可能更少的开销(没有对象-只有字符串等)。

我不知道这个方法的性能统计数据,但是您尝试过使用 Directory.GetFiles()静态方法吗?

它返回一个包含文件名(而不是 FileInfos)的字符串数组,您可以像上面那样检查数组的长度。

在任何情况下,您都必须使用硬盘来获取这些信息,而这一点将胜过任何对象创建和数组填充。

private static void test()
{
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();


string [] dirs = System.IO.Directory.GetDirectories("C:\\Test\\");
string[] files = System.IO.Directory.GetFiles("C:\\Test\\");


if (dirs.Length == 0 && files.Length == 0)
Console.WriteLine("Empty");
else
Console.WriteLine("Not Empty");


sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}

这个快速测试返回2毫秒的文件夹时,空和包含子文件夹 & 文件(5个文件夹,每个5个文件)

我不知道有什么方法可以简洁地告诉你一个给定的文件夹是否包含任何其他文件夹或文件,但是,使用:

Directory.GetFiles(path);
&
Directory.GetDirectories(path);

应该有助于提高性能,因为这两个方法都只返回一个字符串数组,其中包含文件/目录的名称,而不是整个 FileSystemInfo 对象。

谢谢大家的回复。我尝试使用 目录目录方法。好消息!性能提高了 ~ 两倍!221毫秒内打了229通电话。但是我也希望,有可能避免枚举文件夹中的所有项。同意,仍然在执行不必要的作业。你不觉得吗?

经过所有的调查,我得出了一个结论,那就是。NET 进一步优化是不可能的。我将使用 WinAPI 的 FindFirstFile函数。希望能有所帮助。

我的代码是惊人的,它只需要 < strong > 00.00.0007143 文件夹中有34个文件小于毫秒

   System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();


bool IsEmptyDirectory = (Directory.GetFiles("d:\\pdf").Length == 0);


sw.Stop();
Console.WriteLine(sw.Elapsed);

这是我最终实现的一个额外的快速解决方案。这里我使用的是 WinAPI 和函数 FindFirstFileFindNextFile。它允许避免枚举文件夹和 在检测到文件夹中的第一个对象后立即停止中的所有项。这种方法是 ~ 6(! !)比上面描述的要快。36秒内打了250通电话!

private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);


[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct WIN32_FIND_DATA
{
public uint dwFileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string cAlternateFileName;
}


[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
private static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);


[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
private static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);


[DllImport("kernel32.dll")]
private static extern bool FindClose(IntPtr hFindFile);


public static bool CheckDirectoryEmpty_Fast(string path)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException(path);
}


if (Directory.Exists(path))
{
if (path.EndsWith(Path.DirectorySeparatorChar.ToString()))
path += "*";
else
path += Path.DirectorySeparatorChar + "*";


WIN32_FIND_DATA findData;
var findHandle = FindFirstFile(path, out findData);


if (findHandle != INVALID_HANDLE_VALUE)
{
try
{
bool empty = true;
do
{
if (findData.cFileName != "." && findData.cFileName != "..")
empty = false;
} while (empty && FindNextFile(findHandle, out findData));


return empty;
}
finally
{
FindClose(findHandle);
}
}


throw new Exception("Failed to get directory first file",
Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()));
}
throw new DirectoryNotFoundException();
}

我希望它将来对某些人有用。

如果您不介意离开纯 C # 转而使用 WinApi调用,那么可以考虑使用 PathIsDirectoryEmpty () < a href = “ http://msdn.microsoft.com/en-us/library/bb773623.aspx”rel = “ nofollow noReferrer”> PathIsDirectoryEmpty () 函数。根据 MSDN,该功能:

如果 pszPath 是空目录,返回 TRUE。如果 pszPath 不是目录,或者它包含除“”以外的至少一个文件,则返回 FALSE或者”。.".

这似乎是一个功能,完全做你想要的,所以它可能是很好的优化为该任务(虽然我还没有测试)。

要从 C # 调用它,原文地址: http://pinvoke.net/rel = “ nofollow norefrer”> pinvoke.net 站点应该可以帮助您。(不幸的是,它还没有描述这个特定的函数,但是您应该能够找到一些具有类似参数的函数,并在那里返回类型,并将它们作为调用的基础。如果您再次查看 MSDN,它说要从中导入的 DLL 是 shlwapi.dll)

DirectoryDirectoryInfo中有一个新的特征。NET 4,允许它们返回 IEnumerable而不是数组,并且在读取所有目录内容之前开始返回结果。

public bool IsDirectoryEmpty(string path)
{
IEnumerable<string> items = Directory.EnumerateFileSystemEntries(path);
using (IEnumerator<string> en = items.GetEnumerator())
{
return !en.MoveNext();
}
}

编辑: 再次看到这个答案,我意识到这个代码可以变得更简单..。

public bool IsDirectoryEmpty(string path)
{
return !Directory.EnumerateFileSystemEntries(path).Any();
}

我相信其他的答案更快,你的问题是一个文件夹是否包含文件或文件夹... 但我认为大多数时候人们会认为一个目录是空的,如果它不包含文件。即它仍然是“空的”对我来说,如果它包含空的子目录... 这可能不适合您的使用,但可能为其他人!

  public bool DirectoryIsEmpty(string path)
{
int fileCount = Directory.GetFiles(path).Length;
if (fileCount > 0)
{
return false;
}


string[] dirs = Directory.GetDirectories(path);
foreach (string dir in dirs)
{
if (! DirectoryIsEmpty(dir))
{
return false;
}
}


return true;
}

用这个,很简单。

Public Function IsDirectoryEmpty(ByVal strDirectoryPath As String) As Boolean
Dim s() As String = _
Directory.GetFiles(strDirectoryPath)
If s.Length = 0 Then
Return True
Else
Return False
End If
End Function

有时你可能想要验证子目录中是否存在任何文件,而忽略那些空的子目录; 在这种情况下,你可以使用下面的方法:

public bool isDirectoryContainFiles(string path) {
if (!Directory.Exists(path)) return false;
return Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories).Any();
}

我用它来处理文件夹和文件(不知道它是否是最佳的)

    if(Directory.GetFileSystemEntries(path).Length == 0)

这里有一些东西可能会帮助你做到这一点。我设法在两个迭代中做到这一点。

 private static IEnumerable<string> GetAllNonEmptyDirectories(string path)
{
var directories =
Directory.EnumerateDirectories(path, "*.*", SearchOption.AllDirectories)
.ToList();


var directoryList =
(from directory in directories
let isEmpty = Directory.GetFiles(directory, "*.*", SearchOption.AllDirectories).Length == 0
where !isEmpty select directory)
.ToList();


return directoryList.ToList();
}

简单明了:

public static bool DirIsEmpty(string path) {
int num = Directory.GetFiles(path).Length + Directory.GetDirectories(path).Length;
return num == 0;
}

基于 布拉德 · 帕克斯代码:

    public static bool DirectoryIsEmpty(string path)
{
if (System.IO.Directory.GetFiles(path).Length > 0) return false;


foreach (string dir in System.IO.Directory.GetDirectories(path))
if (!DirectoryIsEmpty(dir)) return false;


return true;
}

因为您无论如何都要使用 DirectoryInfo 对象,所以我建议使用扩展名

public static bool IsEmpty(this DirectoryInfo directoryInfo)
{
return directoryInfo.GetFileSystemInfos().Count() == 0;
}