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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/755574/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 12:31:24  来源:igfitidea点击:

How to quickly check if folder is empty (.NET)?

.netiodirectory

提问by zhe

I have to check, if directory on disk is empty. It means, that it does not contain any folders/files. I know, that there is a simple method. We get array of FileSystemInfo's and check if count of elements equals to zero. Something like that:

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

This approach seems OK. BUT!! It is very, very bad from a perspective of performance. GetFileSystemInfos()is a very hard method. Actually, it enumerates all filesystem objects of folder, gets all their properties, creates objects, fills typed array etc. And all this just to simply check Length. That's stupid, isn't it?

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

I just profiled such code and determined, that ~250 calls of such method are executed in ~500ms. This is very slow and I believe, that it is possible to do it much quicker.

我刚刚分析了这样的代码并确定,在~500ms 内执行了~250 次这样的方法调用。这很慢,我相信,可以做得更快。

Any suggestions?

有什么建议?

采纳答案by zhe

Here is the extra fast solution, that I finally implemented. Here I am using WinAPI and functions FindFirstFile, FindNextFile. It allows to avoid enumeration of all items in Folder and stops right after detecting the first object in the Folder. This approach is ~6(!!) times faster, than described above. 250 calls in 36ms!

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

I hope it will be useful for somebody in the future.

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

回答by Thomas Levesque

There is a new feature in Directoryand DirectoryInfoin .NET 4 that allows them to return an IEnumerableinstead of an array, and start returning results before reading all the directory contents.

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

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


EDIT: seeing that answer again, I realize this code can be made much simpler...

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

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

回答by Marc Gravell

You could try Directory.Exists(path)and Directory.GetFiles(path)- probably less overhead (no objects - just strings etc).

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

回答by Eoin Campbell

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

This quick test came back in 2 milliseconds for the folder when empty and when containing subfolders & files (5 folders with 5 files in each)

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

回答by Jmu

I use this for folders and files (don't know if it's optimal)

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

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

回答by akavel

If you don't mind leaving pure C# and going for WinApicalls, then you might want to consider the PathIsDirectoryEmpty()function. According to the MSDN, the function:

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

Returns TRUE if pszPath is an empty directory. Returns FALSE if pszPath is not a directory, or if it contains at least one file other than "." or "..".

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

That seems to be a function which does exactly what you want, so it is probably well optimised for that task (although I haven't tested that).

这似乎是一个完全符合您要求的功能,因此它可能已针对该任务进行了很好的优化(尽管我尚未对此进行测试)。

To call it from C#, the pinvoke.netsite should help you. (Unfortunately, it doesn't describe this certain function yet, but you should be able to find some functions with similar arguments and return type there and use them as the basis for your call. If you look again into the MSDN, it says that the DLL to import from is shlwapi.dll)

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

回答by Cerebrus

I don't know about the performance statistics on this one, but have you tried using the Directory.GetFiles()static method ?

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

It returns a string array containing filenames (not FileInfos) and you can check the length of the array in the same way as above.

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

回答by Brad Parks

I'm sure the other answers are faster, and your question asked for whether or not a folder contained files or folders... but I'd think most of the time people would consider a directory empty if it contains no files. ie It's still "empty" to me if it contains empty subdirectories... this may not fit for your usage, but may for others!

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

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

回答by Don Reba

You will have to go the hard drive for this information in any case, and this alone will trump any object creation and array filling.

在任何情况下,您都必须通过硬盘获取这些信息,仅此一项就胜过任何对象创建和数组填充。

回答by CraigTP

I'm not aware of a method that will succinctly tell you if a given folder contains any other folders or files, however, using:

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

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

should help performance since both of these methods will only return an array of strings with the names of the files/directories rather than entire FileSystemInfo objects.

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