如何快速检查文件夹是否为空(.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(); } 

这种方法似乎没问题。 但!! 从表演的angular度来看,这是非常非常糟糕的。 GetFileSystemInfos()是一个非常困难的方法。 实际上,它枚举文件夹的所有文件系统对象,获取所有的属性,创build对象,填充types数组等等。所有这些只是简单地检查长度。 这很愚蠢,不是吗?

我只是简单地描述了这样的代码,并确定〜250个这样的方法调用在〜500ms内执行。 这是非常缓慢的,我相信,这可以做得更快。

有什么build议么?

.NET 4中的Directory和DirectoryInfo中有一个新function,允许返回一个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(); } 

这是额外的快速解决scheme,我终于实现了。 这里我使用的是WinAPI,它的function是FindFirstFileFindNextFile 。 它允许避免枚举文件夹中的所有项目,并在检测到文件夹中的第一个对象后立即停止 。 这种方法比上面描述的快6倍(!!)倍。 在36ms内拨打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(); } 

我希望这对将来的人有用。

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

这个快速testing在2毫秒内为文件夹空时和包含子文件夹和文件(每个文件夹5个文件5个文件夹)

你可以尝试Directory.Exists(path)Directory.GetFiles(path) – 可能更less的开销(没有对象 – 只是string等)。

如果你不介意留下纯粹的C#和WinApi调用,那么你可能要考虑PathIsDirectoryEmpty()函数。 根据MSDN,function:

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

这似乎是一个正是你想要的function,所以它可能已经很好的完成了这个任务(虽然我还没有testing过)。

要从C#调用它, pinvoke.net网站应该可以帮到你。 (不幸的是,它并没有描述这个特定的function,但是你应该能够find一些具有相似参数的函数并返回types,并用它们作为调用的基础。如果再看一下MSDN,它说要导入的DLL是shlwapi.dll

我不知道关于这一个的性能统计,但你有没有尝试过使用Directory.GetFiles()静态方法?

它返回一个包含文件名(不是FileInfos)的string数组,你可以像上面一样检查数组的长度。

我用这个文件夹和文件(不知道是否是最佳的)

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

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

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

在任何情况下,你都必须去硬盘驱动这个信息,而这仅仅是一个胜过任何对象创build和数组填充。

我不知道一种方法,可以简单地告诉你,如果一个给定的文件夹包含任何其他文件夹或文件,但是,使用:

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

应该有助于提高性能,因为这两种方法都只会返回带有文件/目录名称的string数组,而不是整个FileSystemInfo对象。

谢谢大家的回复。 我试图使用Directory.GetFiles()Directory.GetDirectories()方法。 好消息! 性能提高了两倍! 229个电话221毫秒。 但我也希望,可以避免文件夹中的所有项目的枚举。 同意,这仍然是不必要的工作正在执行。 你不这么认为吗?

经过一番调查,我得出的结论是,在纯.NET下进一步优化是不可能的。 我将玩WinAPI的FindFirstFile函数。 希望它会有所帮助。

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

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

你也应该把你的testing包装到一个try / catch块中,以确保你正确地处理了一个DirectoryNotFoundExceptionexception。 这是一个经典的竞争条件,以防在您检查文件夹是否存在之后立即删除文件夹。

这可能会帮助你做到这一点。 我设法做了两次迭代。

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

我的代码是惊人的,它只需要00: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); 

用这个。 这很简单。

 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