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.