Rekursive Dateisuche mit anonymer Methode

    • VB.NET

    Es gibt 2 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

      Rekursive Dateisuche mit anonymer Methode

      Hiermal eine elegante Implementation einer rekursiven Datei- und Ordner-Suche:

      VB.NET-Quellcode

      1. Private Sub CollectFilesAndFolders(ByVal rootDirs As IEnumerable(Of DirectoryInfo), _
      2. ByVal folderCollector As ICollection(Of DirectoryInfo), _
      3. ByVal fileCollector As ICollection(Of FileInfo), _
      4. Optional ByVal pattern As String = "*.*")
      5. 'rekursive anonyme Methode
      6. Dim recurse As Action(Of IEnumerable(Of DirectoryInfo)) = _
      7. Sub(dirs As IEnumerable(Of DirectoryInfo))
      8. For Each dirinf In dirs
      9. Dim files As IEnumerable(Of FileInfo)
      10. Try
      11. files = dirinf.EnumerateFiles(pattern)
      12. Catch ex As UnauthorizedAccessException
      13. 'für manche Directories hat das Prog keine Rechte
      14. Continue For
      15. End Try
      16. For Each fileInf In files
      17. fileCollector.Add(fileInf)
      18. Next
      19. folderCollector.Add(dirinf)
      20. 'selbst-aufruf
      21. recurse(dirinf.EnumerateDirectories)
      22. Next
      23. End Sub
      24. 'anonyme Methode aufrufen
      25. recurse(rootDirs)
      26. End Sub

      Aufruf-Beispiel (Form mit 2 Listboxen):

      VB.NET-Quellcode

      1. Private Sub GetFilesAndFolders(ByVal rootPath As String)
      2. Dim folders As New List(Of DirectoryInfo)
      3. Dim files As New List(Of FileInfo)
      4. Dim rootDirs = {New DirectoryInfo(rootPath)} 'rootDirs enthält hier nur 1 Directory
      5. CollectFilesAndFolders(rootDirs, folders, files, txtPattern.Text)
      6. 'Collect-Ergebnisse verwenden
      7. lstDirectories.DataSource = folders
      8. lstFiles.DataSource = files
      9. End Sub



      Erläuterungen
      • die Methode CollectFilesAndFolders() nimmt als root nicht nur ein DirectoryInfo, sondern gleich viele - kann also mehrere unzusammenhängende Ordner in einem Aufwasch durchsuchen. Will man nur einen Ordner durchsuchen, gibt man ihr halt eine Auflistung mit nur einem DirectoryInfo - etwa ein Array zu deklarieren erfordert ja nur 2 zusätzliche Zeichen {}:

        VB.NET-Quellcode

        1. Dim rootDirs = {New DirectoryInfo(rootPath)}
        Das ist nur minimal aufwändiger, hält aber o.g. Option offen.

      • Die Methode legt sich nicht auf bestimmte Auflistungen fest, sondern arbeitet auf den allgemeinst-möglichen Interfaces (IEnumerable(Of T), ICollection(Of T)). Dadurch kann man ihr als rootDirs Arrays, Lists, BindingLists, Queues, Stacks - egal was geben - es muß nur IEnumerable(Of DirectoryInfo) implementieren
        Auch die Collectoren sind flexibel - es müssen nur Auflistungen sein, die über eine .Add-Methode verfügen - ICollections eben.

      • 2010 - spezifisch ist die anonyme rekursive Methode, die in CollectFilesAndFolders() eingebaut ist.
        Vorteil dieser Technologie ist, dass die rekursive Methode Zugriff auf die lokalen Variablen der Umgebung hat, sodaß folderCollector und fileCollector ohne jeden Umstand befüllt werden können, ebenso wie auch der pattern zugreifbar ist - egal, in welcher Verschachtelungstiefe die Rekursion grade läuft.
        Wer schonmal eine Rekursion programmiert hat, kennt die Umständlichkeit, dass man entweder extra-Klassenvariablen einführen muß - und damit gegen das Kapselungs-Prinzip verstößt - oder man muß den Selbst-Aufruf sehr umständlich gestalten, nämlich folderCollector, fileCollector und pattern jedesmal als Parameter reinreichen, damit sie in der Rekursion verfügbar sind.
        Bei einer rekursiven anonymen Methode sind 2 Dinge zu beachten
        1. Option Infer failt, also die Signatur muß explizit deklariert sein (hier: Action(Of IEnumerable(Of DirectoryInfo))).
          Ansonsten meckert der Compiler, wenn die anonyme Methode sich selbst aufrufen will.
        2. Erst die rekursive anonyme Methode ausprogrammieren, danach(!) kann man sie auch aufrufen


      Update, Erweiterung
      Habe die Geschichte nunmehr in 3 verschiedenen Views implementiert
      1. per DatagridView und EinzelblattView, mit einer ObjectBindingSource
      2. Mit Listboxen sowohl für Directories als auch Files
      3. mit Treeview mit Checkboxen für Directories
      Das Bildchen zeigt einige angewählte Directories, und wie sie in einer Messagebox ausgegeben werden.


      Wichtiger Hinweis
      Die Solution muß zunächst mal kompiliert werden (Menü - Erstellen), bevor man das MainForm im Designer angugge kann. Denn auf dem MainForm sind UserControls plaziert, und der Form-Designer kann die nur anzeigen, wenn sie als dll vorliegen.

      Nachtrag 11-2014
      Hier eine geschwindigkeits-optimierte Variante (ca. 3 * schneller) von @simpleSoft - vielen Dank! :thumbsup:
      Dateien

      Dieser Beitrag wurde bereits 13 mal editiert, zuletzt von „ErfinderDesRades“ ()

      Hi,

      kurz als Info:
      Es gibt auch noch einen Fehler den du nicht behandelt hast... Ein Ordnerpfad kann "zu lang" sein. Das solltest du noch berücksichtigen. ^^
      Ansonsten nimmt das ne Menge Arbeit ab :D
      Es war einmal ein kleiner Bär... der wollte eine Geschichte hörn... Da erzählte ihm seine Mutti:
      Es war einmal ein kleiner Bär... der wollte eine Geschichte hörn... Da erzählte ihm seine Mutti:
      Es war einmal ein kleiner Bär... der wollte eine Geschichte hörn... Da erzählte ihm seine Mutti:
      ... Nun solltest es selber wissen. :'D

      Erweiterung: rekursive Suche, die beim ersten Treffer abbricht

      Die in der Überschrift gestellte Aufgabe wirft das Problem auf, dass eine Rekursion beliebig tief im Selbst-Aufruf verschachtelt sein kann.
      Das bedeutet, bei einem Treffer kehrt der Return-Aufruf nicht zum eigentlichen Aufrufer zurück, sondern man gelangt nur eine Ebene höher.
      Da es aber unwirtschaftlich wäre, die Suche nach dem Treffer noch fortzuführen, muss man etwas schaffen, dass sie auf allen Ebenen abgebrochen wird, nicht nur auf der Ebene, wo der Treffer auftrat.

      Ja, grad für sowas ist lokale Rekursion besonders geeignet, denn man kann eine Ergebnis-Variable ausserhalb der Rekursion lagern, und die Funktion returnt (egal auf welcher Ebene), wann immer sie feststellt, dass in der Variablen was drinne ist:

      VB.NET-Quellcode

      1. Private Sub btFindFirst_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btFindFirst.Click
      2. Dim fi = FindFile({New DirectoryInfo(FolderBrowserDialog1.SelectedPath)}, txtFindPattern.Text)
      3. If fi Is Nothing Then MessageBox.Show("nix gefunden") Else MessageBox.Show(fi.FullName)
      4. End Sub
      5. Private Function FindFile(ByVal rootDirs As IEnumerable(Of DirectoryInfo), ByVal pattern As String) As FileInfo
      6. Dim fi As FileInfo = Nothing
      7. Dim recurse As Action(Of IEnumerable(Of DirectoryInfo)) = _
      8. Sub(dirs As IEnumerable(Of DirectoryInfo))
      9. For Each dirinf In dirs
      10. Try
      11. fi = dirinf.EnumerateFiles(pattern).FirstOrDefault
      12. If fi IsNot Nothing Then Return
      13. Catch ex As UnauthorizedAccessException
      14. Continue For 'für manche Directories hat das Prog keine Rechte
      15. End Try
      16. recurse(dirinf.EnumerateDirectories) 'selbst-aufruf
      17. If fi IsNot Nothing Then Return
      18. Next
      19. End Sub
      20. recurse(rootDirs)
      21. Return fi
      22. End Function
      Code-Update in post#1, die Funktionalität findet sich auf uclDatagridView

      Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „ErfinderDesRades“ ()