IT

폴더가 비어 있는지 빠르게 확인하는 방법 (.NET)?

lottoking 2020. 7. 7. 07:25
반응형

폴더가 비어 있는지 빠르게 확인하는 방법 (.NET)?


디스크의 디렉토리가 비어 있는지 확인해야합니다. 이는 폴더 / 파일이 포함되어 있지 않음을 의미합니다. 나는 간단한 방법이 있다는 것을 알고 있습니다. FileSystemInfo의 배열을 가져 와서 요소 개수가 0인지 확인합니다. 그런 것 :

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 () 는 매우 어려운 방법입니다. 실제로 폴더의 모든 파일 시스템 객체를 열거하고 모든 속성을 가져오고 객체를 만들고 유형이 지정된 배열을 채 웁니다.이 모든 것은 단순히 길이를 확인하는 것입니다. 어리석지 않습니까?

방금 그러한 코드를 프로파일 링하고 ~ 250ms의 메소드 호출이 ~ 500ms에서 실행된다고 결정했습니다. 이것은 매우 느리며 훨씬 빨리 할 수 ​​있다고 믿습니다.

어떤 제안?


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

여기에 마지막으로 구현 한 추가 빠른 솔루션이 있습니다. 여기에서는 WinAPI 및 FindFirstFile , FindNextFile 함수를 사용하고 있습니다 . Folder의 모든 항목을 열거하지 않고 Folder 의 첫 번째 객체를 감지 한 직후 중지합니다 . 이 방법은 위에서 설명한 것보다 ~ 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();
}

앞으로 누군가에게 도움이 되길 바랍니다.


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)


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

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

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

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 "..".

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.net site 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)


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

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


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

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.


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.


Easy and simple:

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

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

Thanks, everybody, for replies. I tried to use Directory.GetFiles() and Directory.GetDirectories() methods. Good news! The performance improved ~twice! 229 calls in 221ms. But also I hope, that it is possible to avoid enumeration of all items in the folder. Agree, that still the unnecessary job is executing. Don't you think so?

After all investigations, I reached a conclusion, that under pure .NET further optimiation is impossible. I am going to play with WinAPI's FindFirstFile function. Hope it will help.


Some time you might want to verify whether any files exist inside sub directories and ignore those empty sub directories; in this case you can used method below:

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

Based in Brad Parks code:

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

You should also wrap your test into a try/catch block in order to make sure you properly handle a DirectoryNotFoundException. This is a classical race condition in case the folder gets deleted right after you having checked whether it existed.


Here is something that might help you doing it. I managed to do it in two iterations.

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

My code is amazing it just took 00:00:00.0007143 less than milisecond with 34 file in folder

   System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
    sw.Start();

     bool IsEmptyDirectory = (Directory.GetFiles("d:\\pdf").Length == 0);

     sw.Stop();
     Console.WriteLine(sw.Elapsed);

Use this. It's simple.

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

참고URL : https://stackoverflow.com/questions/755574/how-to-quickly-check-if-folder-is-empty-net

반응형