Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

.net

directory

io

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

  • What's New in the BCL in .NET 4 Beta 1
  • Directory.EnumerateFileSystemEntries method overloads
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();
}

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!

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.


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


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)