![]() |
Dateisuche - rekursiv
Bei meiner Suche nach einer guten Routine zur rekursiven Dateisuche bin ich über die Suchfunktion hier auf den Vorschlag von SirThornberry gestoßen, der einerseits schnell ist und andererseits auch (durch die Überprüfung mit Like()) die "Fehlfunde" (z.B. gesucht wird '*.fla', gefunden wird von FindFirst/Next auch 'test.flac') beseitigt:
Delphi-Quellcode:
Desweiteren bin ich auf die Komponente "
procedure FindAllFiles (FileList: TStrings;
RootFolder: string; Mask: string='*'; Recurse: Boolean=True; AddFolderNames: Boolean=False; IgnoreMaskAtFolderNames: Boolean=True); procedure LFindAllFiles(aParentFolder: String); var SR: TSearchRec; begin if FindFirst(aParentFolder+'*', faAnyFile, SR) = 0 then begin repeat if (SR.Name <> '.') and (SR.Name <> '..') then begin if (SR.Attr and faDirectory) = faDirectory then begin if AddFolderNames and (IgnoreMaskAtFolderNames or Like(AnsiLowerCase(SR.Name),Mask)) then FileList.AddObject(aParentFolder+SR.Name, TObject(True)); if Recurse then LFindAllFiles(aParentFolder+SR.Name+'\'); end else if Like(AnsiLowerCase(SR.Name),Mask) then FileList.AddObject(aParentFolder+SR.Name, TObject(False)); end; until (FindNext(SR) <> 0); FindClose(SR); end; end; begin Mask := AnsiLowerCase(Mask); LFindAllFiles(IncludeTrailingPathDelimiter(RootFolder)); end; ![]() Zitat:
Delphi-Quellcode:
Mein Vorschlag für eine Next-Methode (ohne Filterung der Dateinamen) wäre dann so was:
FFBasic.SearchFolder := edDirName.Text;
while FFBasic.Next do begin Memo1.Lines.Add(FFBasic.FullName); end;
Delphi-Quellcode:
Problem: Mir will einfach kein sauberer Weg einfallen, wie man mit so einem Ansatz (und ohne eine interne Stringliste zu verwenden) das Ganze rekursiv auch in den Unterverzeichnissen suchen lassen kann?
function TFileFinder.Next: Boolean;
begin if (FFileCount = 0) then result := (SysUtils.FindFirst(FSearchFolder+'*', faAnyFile, FSR) = 0) else result := (SysUtils.FindNext(FSR) = 0); if result then begin Inc(FFileCount); FFileName := FSR.Name; FFullName := FSearchFolder + FFileName; end else SysUtils.FindNext(FSR); end; Ich will hier auch nicht die €30 für die Komponente sparen; mich würde nur die (professionelle?) Lösung dieses Problems interessieren... .sundance. |
Re: Dateisuche - rekursiv
Wenn du das FindAllFiles von einer Rekursion auf eine Iteration umstellst, dann sollte sich da "leicht etwas machen lassen,
dann kannst du leicht mittendrinnen rausspringen, also die Suchfunktion verlassen und später (in .Next) neu einsteigen. Würde dann in entwa auf ein ähnliches Verfahren rauskommen, wie beim SAX-Parser in der alten Version meines himXML (in der Neuen ist er noch nicht veröffentlicht). [add] einen Anfäng gäbe es dort ![]() |
Re: Dateisuche - rekursiv
Zitat:
(ich wollte dich gerade nach einem iterativen Beispiel fragen, als ich bemerkt habe, dass das nicht unbedingt trivial ist :thumb:) Vielen Dank erstmal; ich werde das mal "durcharbeiten"... .sundance. |
Re: Dateisuche - rekursiv
Das Ganze komplett auf Callback umzustellen geht noch einfach.
Dieses ginge auch bei der rekursiven Lösung genauso einfach.
Delphi-Quellcode:
Hier würde man die Funktion aufrufen und würde jede gefundene Datei sofort
Type TFAFCallback = Procedure(Const Filename: String; Count: Integer);
Function FindAllFilesCallback(Callback: TFAFCallback; Const Dir: String; Const Mask: String = '*.*'; Recurse: Boolean = True): Integer; Var MaskN, MaskE: String; i: Integer; A: Array of Record SR: TSearchRec; Dir: String; End; Begin Result := 0; MaskN := ChangeFileExt(Mask, ''); MaskE := ExtractFileExt(Mask); If MaskE <> '' Then Delete(MaskE, 1, 1) Else If Mask = '*' Then MaskE := '*'; SetLength(A, 32); A[0].Dir := IncludeTrailingPathDelimiter(Dir); i := 0; Repeat If FindFirst(A[i].Dir + '*.*', faAnyFile, A[i].SR) = 0 Then Repeat If (A[i].SR.Name <> '.') and (A[i].SR.Name <> '..') Then If A[i].SR.Attr and faDirectory = 0 Then Begin If MatchText(MaskN, ChangeFileExt(A[i].SR.Name, '')) and MatchText(MaskE, Copy(ExtractFileExt(A[i].SR.Name), 2, 888)) Then Begin Inc(Result); Callback(A[i].Dir + A[i].SR.Name, Result); End; End Else If Recurse Then Begin Inc(i); If i > High(A) Then SetLength(A, (i + 32) and not 31); A[i].Dir := A[i - 1].Dir + A[i - 1].SR.Name + '\'; Break; End; While (i >= 0) and (FindNext(A[i].SR) <> 0) do Begin FindClose(A[i].SR); Dec(i); End; Until i < 0; Until i <= 0; End; im TFAFCallback geliefert bekommen. |
Re: Dateisuche - rekursiv
![]() Hab das "Next" aber andersumgesetzt. Vorallem das das "Next" beim TFileFinder ein bissel verwirrend benannt ist, da man es auch zum Starten als "First" nutzt. :shock: |
Re: Dateisuche - rekursiv
Hallo himitsu,
vielen Dank nochmal für die "Gedankenstütze"... Ich bastle gerade selber einen Algorithmus (teste gerade einen Ansatz mit state machine) und hätte da noch eine Frage zu deinem: Du "konstruierst" jetzt den Datennamen mit der Funktion "Filename" aus den SearchRec.Name-Teilen (anstelle einer zus. Variable im Array) und hast auch die Variable, die den Level der Unterverzeichnisebene darstellt, entfallen lassen und bestimmst diesen Wert durch High(_List). Gibt's dafür einen besonderen Grund? Ich frage nur deshalb, weil ja eigentlich der Ansatz, das Array mit SetLength() nur in 32er-Schritten zu vergrößern, ganz gut klingt (TStrings macht es ja auch so), und mir das Ablegen eines Verzeichnisnamens im "Level-Array" auch performancemäßig besser erscheint. .sundance. |
Re: Dateisuche - rekursiv
'nen besonderen Grund gibt's nicht, aber
- ich seh in einer Optimierung keinen Vorteil, da hier der Datenträger mehr ausbremst, als das "unoptimalere" Speichermanagement - dann hat man heutzutage einen "optimaleren" Speichermanage (FastMM) in Delphi integriert (OK, in Delphi 7 ist der noch nicht standardmäßig drinnen), welcher praktisch deine "32er-Schritte" erledigt und ich somit diese Speicheroptimierung einfach vom MM erledigen lasse, anstatt es selber zu implementieren (hat man gleich noch eine Variable eingespart) - und Zuletzt: ich war Faul und hab mir diese Optimierng gespart (dazumal sie ja eh nicht viel bringt ... siehe Punkt 1 und 2) Ja und das mit dem "Dateipfaden aus den SearchRecs zusammenbauen", da kann man geteilte Meinung sein. Hat Beides vor und Nachteile. Pfade zusammenbauen: + geringerer Speicher, da man nirgends die langen Namen speichern muß - braucht ein paar Nanosekunden länger, da man diese Namen erst zusammensetzen muß Pfade speichern: - mehr Speicher, da ja irgendwo diese Strings gespeichert werden müssen + ist ein paar Nanosekunden (pro Auslesevorgang) schneller, da die Namen schon fertig zusammengesetzt und somit direkt abrufbar sind. Wobei das Mehr an Speicher hier wohl nicht viel auffällt, da ja die Pfadtiefe nicht extrem groß ausfallen dürfte. Wie gesagt, der "langsame" Datenträger bremst eh dermaßen aus, daß Optimierungen im "superschnellen" Arbeitsspeicher nicht wirklich auffallen dürften. :angel: Das ist praktisch der tägliche Kampf: > lohnen sich Optimierungen und wenn ja, wie viel lohnt sich wirklich Man kann sich ja soweit zu Tote optimieren, daß da massig Zeit draufgeht und am Ende der Code auch noch unübersichtlicher und schlechter wartbar wird. |
Re: Dateisuche - rekursiv
Hallo himitsu,
ich habe meine Next-Methode jetzt fertiggebastelt (der Übersichtlichkeit halber habe ich ein paar lokale Variablen zum Zwischenspeicher des Arrayindex und des Dateinamens verwendet); es ist jetzt doch keine reinrassige state-machine geworden. Zum Start der Suche wird vorausgesetzt, dass dass dyn. Array FSR die Größe 1 hat, FNewLevel = true ist und in FCurrentDir das Startverzeichnis abgelegt ist:
Delphi-Quellcode:
Wie du siehst, habe ich deine Ideen (kein Mitschreiben des aktuellen Verzeichnisses und kein blockweises Alloziieren des dynamischen Arrays) übernommen. Über die Funktion FileNameMatches() kann die gefundene Datei anhand beliebiger Kriterien (WildCard, Attribute, RegEx, Größe) überprüft werden.
function TFileFinder.Next: Boolean;
var searchOK: Boolean; fn: String; i,k: Integer; begin i := High(FSR); // Aktuelle Verzeichnisebene über Array-Größe ermitteln repeat if (FNewLevel) then begin // Suche geht weiter/beginnt bei einem neuen Verzeichnis: searchOK := (FindFirst(FCurrentDir+'*', faAnyFile, FSR[i]) = 0); FNewLevel := false; end else // Suche geht weiter im letzten Verzeichnis: searchOK := (FindNext(FSR[i]) = 0); if searchOK then begin // Suche war erfolgreich... fn := FSR[i].Name; if (FSR[i].Attr and faDirectory <> 0) then begin // Verzeichnis gefunden: if (fn <> '.') and (fn <> '..') then begin StoreFileData(FSR[i]); if FRecursiveSearch then begin FNewLevel := true; Inc(i); SetLength(FSR,succ(i)); FCurrentDir := FCurrentDir + fn + '\'; end; if FIncludeFolderNames then Break; end; end else begin // Datei gefunden: if FileNameMatches(FSR[i]) then begin StoreFileData(FSR[i]); Inc(FFileCount); Break; end; end; end else begin // keine weiteren Verzeichniseinträge mehr vorhanden... FindClose(FSR[i]); Dec(i); SetLength(FSR,succ(i)); FCurrentDir := FSearchFolder; for k:=0 to pred(i) do FCurrentDir := FCurrentDir + FSR[k].Name + '\'; end; until (i < 0); result := (i >= 0); end; Sobald meine Klasse TFileFinder ganz fertig ist, werde ich das hier mal posten... .sundance. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:46 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz