Jak szybko sprawdzić, czy folder jest pusty (. NET)?

Muszę sprawdzić, czy katalog na dysku jest pusty. Oznacza to, że nie zawiera żadnych folderów/plików. Wiem, że istnieje prosta metoda. Otrzymujemy array of FileSystemInfo ' s i sprawdzamy czy liczba elementów jest równa zeru. Coś w tym stylu:

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

To podejście wydaje się OK. Ale!! Jest bardzo, bardzo źle z punktu widzenia wydajności. GetFileSystemInfos () jest bardzo trudną metodą. W rzeczywistości wylicza wszystkie obiekty systemu plików folderu, pobiera wszystkie ich właściwości, wytworzy obiekty, wypełni wpisaną tablicę itd. A wszystko po to, aby po prostu sprawdzić Długość. To głupie, prawda?

Właśnie wyprofilowałem taki kod i stwierdziłem, że ~250 wywołań takiej metody jest wykonywane w ~500ms. jest to bardzo powolne i wierzę, że można to zrobić znacznie szybciej.

Jakieś sugestie?
 111
Author: Noam M, 2009-04-16

17 answers

Istnieje nowa funkcja w Directory i DirectoryInfo w. Net 4, która pozwala zwracać liczbę liczbową zamiast tablicy i rozpoczyna zwracanie wyników przed odczytaniem całej zawartości katalogu.

Zobacz tutaj i tam

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

EDIT: widząc tę odpowiedź ponownie, zdaję sobie sprawę, że ten kod można znacznie uprościć...

public bool IsDirectoryEmpty(string path)
{
    return !Directory.EnumerateFileSystemEntries(path).Any();
}
 228
Author: Thomas Levesque,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-14 18:21:25

Oto bardzo szybkie rozwiązanie, które w końcu wdrożyłem. Tutaj używam WinAPI i functions FindFirstFile, FindNextFile . Pozwala to uniknąć wyliczenia wszystkich elementów w folderze i zatrzymuje się zaraz po wykryciu pierwszego obiektu w folderze . Takie podejście to ~6 (!!) razy szybciej, niż opisano powyżej. 250 połączeń w 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();
}

Mam nadzieję, że będzie to przydatne dla kogoś w przyszłości.

 26
Author: zhe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-22 13:52:46
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);
}

Ten szybki test wrócił w ciągu 2 milisekund dla folderu, gdy jest pusty i gdy zawiera podfoldery i pliki (5 folderów z 5 plikami w każdym)

 19
Author: Eoin Campbell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-16 10:56:38

Możesz spróbować Directory.Exists(path) i Directory.GetFiles(path) - prawdopodobnie mniej napowietrznych (bez obiektów-tylko ciągi itp.).

 16
Author: Marc Gravell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-16 10:54:13

Używam tego do folderów i plików (Nie wiem, czy jest optymalny)

    if(Directory.GetFileSystemEntries(path).Length == 0)
 9
Author: Jmu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-30 07:56:55

Jeśli nie masz nic przeciwko pozostawieniu czystego C# i uruchomieniu WinAPI, możesz rozważyć PathIsDirectoryEmpty() funkcja. Zgodnie z MSDN funkcja:

Zwraca TRUE, jeśli pszpath jest pustym katalogiem. Zwraca FALSE, jeśli pszPath nie jest katalogiem lub jeśli zawiera co najmniej jeden plik inny niż "."lub"..".

Wydaje się, że jest to funkcja, która robi dokładnie to, co chcesz, więc prawdopodobnie jest dobrze zoptymalizowana do tego zadanie (choć tego nie testowałem).

Aby wywołać go z C#, the pinvoke.net strona powinna Ci pomóc. (Niestety, nie opisuje jeszcze tej funkcji, ale powinieneś być w stanie znaleźć tam funkcje o podobnych argumentach i zwracanym typie i użyć ich jako podstawy do wywołania. Jeśli spojrzysz ponownie do MSDN, to mówi, że DLL do importowania z jest shlwapi.dll)

 8
Author: akavel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-27 20:51:17

Nie znam się na statystykach wydajności, ale czy próbowałeś użyć metody Directory.GetFiles() statycznej ?

Zwraca tablicę łańcuchową zawierającą nazwy plików (nie FileInfos) i można sprawdzić długość tablicy w taki sam sposób jak powyżej.

 7
Author: Cerebrus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-16 10:55:15

Jestem pewien, że inne odpowiedzi są szybsze, a twoje pytanie zadane, czy folder zawiera pliki lub foldery... ale myślę, że większość ludzi uważa katalog za pusty, jeśli nie zawiera plików. ie jest nadal "pusty" dla mnie, jeśli zawiera puste podkatalogi... może to nie pasować do Twojego użytku, ale może dla innych!

  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;
  }
 4
Author: Brad Parks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-21 19:34:23

Będziesz musiał przejść do dysku twardego dla tych informacji w każdym przypadku, i to samo przebije wszelkie tworzenie obiektów i wypełnianie tablic.

 3
Author: Don Reba,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-16 10:55:56

Nie znam metody, która zwięźle powie Ci, czy dany folder zawiera inne foldery lub pliki, jednak używając:

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

Powinna pomóc w wydajności, ponieważ obie te metody zwrócą tylko tablicę łańcuchów z nazwami plików/katalogów, a nie całych obiektów FileSystemInfo.

 3
Author: CraigTP,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-16 10:56:44

Dzięki wszystkim za odpowiedzi. Próbowałem użyć katalogu .Katalog GetFiles () i .Metody GetDirectories () . Dobre wieści! Wydajność poprawiła się ~ dwukrotnie! 229 wywołań w 221ms. ale również mam nadzieję, że można uniknąć wyliczenia wszystkich pozycji w folderze. Zgadzam się, że nadal niepotrzebna praca jest wykonywana. Nie sądzisz?

Po wszystkich badaniach doszedłem do wniosku, że pod czystym. NET dalsza optymalizacja jest niemożliwa. Mam zamiar pobawić się z Funkcja FindFirstFile WinAPI. Mam nadzieję, że to pomoże.

 2
Author: zhe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-16 11:29:38

Jakiś czas możesz sprawdzić, czy w podkatalogach istnieją jakieś pliki i zignorować te puste podkatalogi; w tym przypadku możesz użyć metody poniżej:

public bool isDirectoryContainFiles(string path) {
    if (!Directory.Exists(path)) return false;
    return Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories).Any();
}
 2
Author: Leng Weh Seng,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-22 02:36:27

Łatwe i proste:

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

if (num == 0)
{
   //empty
}
 2
Author: Matheus Miranda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-23 00:32:33

Powinieneś również zawinąć test w blok try/catch, aby upewnić się, że poprawnie obsługujesz DirectoryNotFoundException. Jest to klasyczny stan wyścigu w przypadku, gdy folder zostanie usunięty zaraz po sprawdzeniu, czy istniał.

 0
Author: Philipp Sumi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-27 17:34:23

Oto coś, co może Ci w tym pomóc. Udało mi się to zrobić w dwóch iteracjach.

 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();
   }
 0
Author: Gabriel Marius Popescu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-06 08:56:45

Mój kod jest niesamowity, że po prostu 00:00:00.0007143 mniej niż milisekunda z 34 plikami w folderze

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

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

     sw.Stop();
     Console.WriteLine(sw.Elapsed);
 -1
Author: Prashant C,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-16 11:40:52

Użyj tego. To proste.

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
 -2
Author: Puffgroovy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-06 18:24:17