IT-Swarm.Net

如何快速检查文件夹是否为空(.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内执行。这是非常缓慢的,我相信,它可以更快地完成。.

有什么建议?

122
zhe

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

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

28
zhe

快速,可靠且价格合理的云托管

注册并在30天内获得$50奖金!

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

当空文件夹和包含子文件夹和文件(5个文件夹,每个文件夹包含5个文件夹)时,此快速测试在2毫秒内返回

19
Eoin Campbell

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

18
Marc Gravell

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

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

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

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

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

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

8
akavel

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

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

7
Cerebrus

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

  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;
  }
4
Brad Parks

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

3
Don Reba

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

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

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

3
CraigTP

简单易用:

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

if (num == 0)
{
   //empty
}
3
Matheus Miranda

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

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

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

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

2
zhe

位于 Brad Parks 代码:

    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;
    }
1
Ángel Ibáñez

这可能有助于你做到这一点。我设法在两次迭代中完成。.

 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();
   }
0
Gabriel Marius Popescu

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

0
Philipp Sumi