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

时间:2009-04-16 10:45:14

标签: .net io directory

我必须检查磁盘上的目录是否为空。这意味着,它不包含任何文件夹/文件。我知道,有一个简单的方法。我们得到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内执行。这是非常缓慢的,我相信,它可以更快地完成。

有什么建议吗?

19 个答案:

答案 0 :(得分:252)

.NET 4中的Directory和DirectoryInfo中有一个新功能允许返回IEnumerable而不是数组,并在读取所有目录内容之前开始返回结果。

See herethere

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

答案 1 :(得分:28)

这是我最终实施的超快解决方案。在这里,我使用WinAPI和函数 FindFirstFile FindNextFile 。它允许避免枚举文件夹中的所有项目,并在检测到文件夹中的第一个对象后立即停止停止。这种方法比上述方法快约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();
}

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

答案 2 :(得分:19)

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个文件)

答案 3 :(得分:18)

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

答案 4 :(得分:10)

我将它用于文件夹和文件(不知道它是否是最佳的)

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

答案 5 :(得分:8)

如果您不介意离开纯C#并进行 WinApi 来电,那么您可能需要考虑 PathIsDirectoryEmpty() 功能。根据MSDN,功能:

  

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

这似乎是一个完全符合你想要的功能,所以它可能已经针对该任务进行了很好的优化(尽管我还没有测试过)。

要从C#中调用它, pinvoke.net 网站应该可以为您提供帮助。 (不幸的是,它还没有描述这个特定的功能,但你应该能够找到一些具有类似参数的函数并返回那里的类型,并将它们用作你的调用的基础。如果再看一下MSDN,它会说要导入的DLL是shlwapi.dll

答案 6 :(得分:7)

我不知道有关此问题的效果统计信息,但您是否尝试过使用Directory.GetFiles()静态方法?

它返回一个包含文件名(不是FileInfos)的字符串数组,你可以用与上面相同的方式检查数组的长度。

答案 7 :(得分:4)

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

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

答案 8 :(得分:3)

我不知道一种方法会简洁地告诉您某个文件夹是否包含任何其他文件夹或文件,但是,使用:

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

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

答案 9 :(得分:3)

在任何情况下,您都必须使用硬盘驱动器来获取此信息,仅此一项将胜过任何对象创建和阵列填充。

答案 10 :(得分:3)

简单易行:

int num = Directory.GetFiles(pathName).Length;

if (num == 0)
{
   //empty
}

答案 11 :(得分:2)

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

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

答案 12 :(得分:2)

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

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

答案 13 :(得分:1)

基于布拉德公园代码:

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

答案 14 :(得分:0)

您还应该将测试包装到try / catch块中,以确保正确处理DirectoryNotFoundException。如果文件夹在您检查文件夹是否存在后立即被删除,这是一种经典的竞争条件。

答案 15 :(得分: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();
   }

答案 16 :(得分:0)

由于您还是要使用DirectoryInfo对象,因此我将使用扩展名

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

答案 17 :(得分:-1)

我的代码很棒,只需 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);

答案 18 :(得分:-2)

使用它。这很简单。

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