Um mit VB.NET ganze Verzeichnisstrukturen mit Unterverzeichnisse rekursiv einlesen zulassen, gibt es einige gute Wege, fertige Klassen und Funktionen. Da ich allerdings mittlerweile bestimmt bald an die 1 Million Objekte (Ordner und Dateien) zu administrativen Zwecken durchsuchen lasse und weiterverarbeite, braucht man doch eine etwas robustere Lösung.
So hab ich einige teils fertige und auch eigene Entwicklungen genutzt, bin dann irgendwie allerdings auf ein recht ordentliche Klasse gestoßen und habe sie meinen Bedürfnissen angepasst und erweitert.
Probleme
Im Laufe der Zeit bin ich auf diverse Probleme gestoßen, die allesamt mittlerweile irgendwie übergangen werden können. :)
- Datei- und Verzeichnisnamen dürfen maximal 255 Zeichen lang sein
- Überspringen von Verzeichnissen und Dateien auf denen man keine Zugriffsberechtigung hat
- Verzeichnisse und Dateinamen mit wirklich sehr ausgefallenen Sonderzeichen
Funktionen
Einige Funktionen sollten kurz genannt werden, finden sich aber auch im Beispiel wieder.
- Es kann eine Maske zur Filter sowohl für Dateien als auch Ordner übergeben werden, wie man es aus der Explorersuche kennt (*.exe;*.zip)
- Die zurückgegebene Ergebnisliste stellt die Werte nicht wie üblich als String Liste bereit, sondern als System.IO Objekt, z.B. eine FileInfo oder Directory Liste
.NET Bordmittel
Natürlich kann man auch direkt die .NET Funktion zum rekursiven einlesen nutzen, für einfache Strukturen vollkommen ausreichend. Bricht bei 255 Zeichen langen Pfaden allerdings konsequent ab.
System.IO.Directory.GetFiles(RootDir, "*.exe", IO.SearchOption.AllDirectories)
/trunk/FileSearchRecursive/example.vb
'$Id: example.vb 11 2010-02-17 06:54:48Z espendiller $ Module example 'or in single line: Dim _Files As New FileSearch(New IO.DirectoryInfo("d:\Media\"), "*.nfo") Dim RecursiveScan As New FileSearch Sub Main() Dim RootDir As String = "C:\Windows" Console.WriteLine("Scanning: " & RootDir) RecursiveScan.Search(RootDir, "*.exe") 'print out scan result Console.WriteLine(RecursiveScan.Files.Count & " Files found, press key for details") Console.ReadKey() 'walk through ArrayList of Files For Each File As System.IO.FileInfo In RecursiveScan.Files Console.WriteLine(File.FullName & " - " & File.Length & " - " & File.LastWriteTime) Next Console.ReadKey() 'you can also use this method to scan recursive but there is no error handling 'System.IO.Directory.GetFiles(RootDir, "*.exe", IO.SearchOption.AllDirectories) End Sub End Module
/trunk/FileSearchRecursive/FileSearch.vb
'$Id: FileSearch.vb 11 2010-02-17 06:54:48Z espendiller $ 'Option Strict On 'Option Explicit On Imports System.IO Imports System.Collections.Specialized ''' <summary> ''' Recursive Scan a Directory for Files with possibility to use File/Directory Masks ''' ''' http://www.vbforums.com/showthread.php?t=341919 ''' </summary> ''' <remarks></remarks> Public Class FileSearch Private Const DefaultFileMask As String = "*.*" Private Const DefaultDirectoryMask As String = "*" #Region " Member Variables " Private _InitialDirectory As DirectoryInfo Private _DirectoryMasks As StringCollection Private _FileMasks As StringCollection Private _Directories As New ArrayList Private _Files As New ArrayList #End Region #Region " Properites " Public Property InitialDirectory() As DirectoryInfo Get Return _InitialDirectory End Get Set(ByVal Value As DirectoryInfo) _InitialDirectory = Value End Set End Property Public Property DirectoryMask() As StringCollection Get Return _DirectoryMasks End Get Set(ByVal Value As StringCollection) _DirectoryMasks = Value End Set End Property ''' <summary> ''' FileMask to filter Files ''' </summary> ''' <value>*.zip or *.zip;*.jpg;*.bmp or </value> ''' <returns></returns> ''' <remarks></remarks> Public Property FileMask() As StringCollection Get Return _FileMasks End Get Set(ByVal Value As StringCollection) _FileMasks = Value End Set End Property ''' <summary> ''' List all Directories that has been found; use Search() first ''' </summary> ''' <value></value> ''' <returns>ArrayList with Directories</returns> ''' <remarks></remarks> Public ReadOnly Property Directories() As ArrayList Get Return _Directories End Get End Property ''' <summary> ''' List all Files that has been found; use Search() first ''' </summary> ''' <value>ArrayList with Files</value> ''' <returns></returns> ''' <remarks></remarks> Public ReadOnly Property Files() As ArrayList Get Return _Files End Get End Property #End Region #Region " Constructors " Public Sub New() End Sub Public Sub New( _ ByVal BaseDirectory As String) Me.New(New DirectoryInfo(BaseDirectory)) End Sub Public Sub New( _ ByVal InitialDirectory As DirectoryInfo) _InitialDirectory = InitialDirectory End Sub #End Region ''' <summary> ''' Perfomer Recursive Search ''' </summary> ''' <param name="InitalDirectory">Parent directory</param> ''' <param name="FileMask">FileMask to filter Files: "*.zip" or "*.zip;*.jpg;*.bmp"</param> ''' <param name="DirectoryMask">Makes for Files</param> ''' <remarks></remarks> Public Overloads Sub Search(ByVal InitalDirectory As String, Optional ByVal FileMask As String = Nothing, Optional ByVal DirectoryMask As String = Nothing) Search(New DirectoryInfo(InitalDirectory), FileMask, DirectoryMask) End Sub Public Overloads Sub Search( _ Optional ByVal InitalDirectory As DirectoryInfo = Nothing, _ Optional ByVal FileMask As String = Nothing, _ Optional ByVal DirectoryMask As String = Nothing) _Files = New ArrayList _Directories = New ArrayList If Not IsNothing(InitalDirectory) Then _InitialDirectory = InitalDirectory End If If IsNothing(_InitialDirectory) Then Throw New ArgumentException("A Directory Must be specified!", "Directory") End If If IsNothing(FileMask) OrElse FileMask.Length = 0 Then _FileMasks = New StringCollection _FileMasks.Add(DefaultFileMask) Else _FileMasks = ParseMask(FileMask) End If If IsNothing(DirectoryMask) OrElse DirectoryMask.Length > 0 Then _DirectoryMasks = New StringCollection _DirectoryMasks.Add(DefaultDirectoryMask) Else _DirectoryMasks = ParseMask(DirectoryMask) End If DoSearch(_InitialDirectory) End Sub Private Sub DoSearch(ByVal BaseDirectory As DirectoryInfo) Try For Each fm As String In _FileMasks Try 'added for directory/file which are longer then 255 Files.AddRange(BaseDirectory.GetFiles(fm)) Catch ex As Exception End Try Next Catch u As UnauthorizedAccessException End Try Try Dim Directories As New ArrayList For Each dm As String In _DirectoryMasks Try 'added for directory/file which are longer then 255 Directories.AddRange(BaseDirectory.GetDirectories(dm)) _Directories.AddRange(Directories) Catch ex As Exception End Try Next For Each di As DirectoryInfo In Directories DoSearch(di) Next Catch u As UnauthorizedAccessException End Try End Sub Private Shared Function ParseMask(ByVal Mask As String) As StringCollection If IsNothing(Mask) Then Return Nothing End If Mask = Mask.Trim(";"c) If Mask.Length = 0 Then Return Nothing End If Dim Masks As New StringCollection Masks.AddRange(Mask.Split(";"c)) Return Masks End Function Protected Overrides Sub Finalize() _Files = Nothing _Directories = Nothing MyBase.Finalize() End Sub End Class
Disqus - noscript
klasse Arbeit, jedoch ein kleiner Fehler...DirectoryMask.Length = 0 wäre richtig :)
If IsNothing(DirectoryMask) OrElse DirectoryMask.Length > 0 Then
_DirectoryMasks = New StringCollection
_DirectoryMasks.Add(DefaultDirectoryMask)
Else
_DirectoryMasks = ParseMask(DirectoryMask)
End If
PS: Die DirectoryMask ist eine Positivliste, könnte mann da noch eine Negativliste einbauen. Damit meine ich Verzeichnisse sollten nicht durchsucht werden.
Gruß
Markus
jawohl das sieht logischer aus da haste recht. werd's bei Gelegenheit mal anpassen, dank.