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

zhe*_*zhe 129 .net directory io

我必须检查磁盘上的目录是否为空.这意味着,它不包含任何文件夹/文件.我知道,有一个简单的方法.我们得到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();
}
Run Code Online (Sandbox Code Playgroud)

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

我只是描述了这样的代码并确定,这种方法的〜250次调用在~500ms内执行.这是非常缓慢的,我相信,它可以更快地完成.

有什么建议?

Tho*_*que 266

.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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

public bool IsDirectoryEmpty(string path)
{
    return !Directory.EnumerateFileSystemEntries(path).Any();
}
Run Code Online (Sandbox Code Playgroud)

  • 甜!喜欢单行代码解决方案.upvoted. (6认同)
  • @Dennis,你可以在调用`EnumerateFileSystemEntries`中指定一个通配符模式,或者使用`.Any(condition)`(将条件指定为lambda表达式,或者将路径作为参数指定的方法). (5认同)

zhe*_*zhe 30

这是我最终实施的超快解决方案.这里我使用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();
}
Run Code Online (Sandbox Code Playgroud)

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

  • 您需要将`SetLastError = true`添加到`FindFirstFile`的`DllImport`中,以便`Marshal.GetHRForLastWin32Error()`调用正常工作,如[MSDN doc for GetHRForLastWin32Error()]的备注部分所述(http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.gethrforlastwin32error.aspx). (3认同)

Mar*_*ell 19

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

  • 但是,对我没有好处; 第一个答案,唯一没有投票的人;-( (2认同)
  • 我不认为 GetFiles 会得到目录列表,所以检查 GetDirectories 似乎是个好主意 (2认同)

Eoi*_*ell 18

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);
}
Run Code Online (Sandbox Code Playgroud)

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

  • 是的,但是如果有数千个文件呢? (3认同)
  • 你可以通过返回'dirs'是非空的直接返回来改进这一点,而不必获取文件列表. (2认同)
  • 您还要测量写入控制台的时间,这是不可忽视的. (2认同)

小智 12

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

    if(Directory.GetFileSystemEntries(path).Length == 0)
Run Code Online (Sandbox Code Playgroud)


aka*_*vel 8

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

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

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

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

  • 对于那些想要走这条路线的人来说.看来shlwapi.dll的这个PathIsDirectoryEmpty()方法在Vista32/64和XP32/64机器上工作正常,但是在一些Win7机器上爆炸了.它必须与不同版本的Windows附带的shlwapi.dll版本有关.谨防. (3认同)

Cer*_*rus 7

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

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


Bra*_*rks 5

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

  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;
  }
Run Code Online (Sandbox Code Playgroud)