[.net] 폴더가 비어 있는지 빠르게 확인하는 방법 (.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에서 실행된다고 결정했습니다. 이것은 매우 느리며 훨씬 빨리 할 수 ​​있다고 믿습니다.

어떤 제안?



답변

새로운 기능이 DirectoryDirectoryInfo그들은 반환 할 수 있습니다 .NET 4의 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();
}

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


답변

시도 Directory.Exists(path)하고 Directory.GetFiles(path)-아마도 오버 헤드가 적습니다 (객체가 아닌 문자열 만).


답변

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 밀리 초로 되돌아 왔습니다.


답변

폴더와 파일에 이것을 사용합니다 (최적인지 모르겠습니다)

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


답변

순수한 C #을 남기지 않고 WinApi 호출 을 수행하는 것이 마음에 들지 않으면 PathIsDirectoryEmpty () 함수 를 고려할 수 있습니다 . MSDN에 따르면 기능은 다음과 같습니다.

pszPath가 빈 디렉토리 인 경우 TRUE를 리턴합니다. pszPath가 디렉토리가 아니거나 “.”이외의 파일이 하나 이상 포함 된 경우 FALSE를 리턴합니다. 또는 “..”.

그것은 당신이 원하는 것을 정확하게 수행하는 함수 인 것 같습니다. 그래서 그것은 아마도 그 작업에 대해 잘 최적화되어 있습니다 (테스트하지는 않았지만).

C #에서 호출하려면 pinvoke.net 사이트가 도움이 될 것입니다. (안타깝게도이 함수는 아직 설명하지 않았지만 비슷한 인수를 가진 함수를 찾아서 리턴 타입을 호출의 기초로 사용할 수 있어야합니다. MSDN을 다시 살펴보면 다음과 같습니다. 가져올 DLL은 shlwapi.dll)입니다.


답변

나는 이것에 대한 성능 통계에 대해 모른다. 그러나 당신은 Directory.GetFiles() 정적 방법을 보셨습니까?

FileInfo가 아닌 파일 이름을 포함하는 문자열 배열을 반환하며 위와 동일한 방식으로 배열의 길이를 확인할 수 있습니다.