AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Dateisuche - rekursiv

Ein Thema von sundance · begonnen am 8. Dez 2009 · letzter Beitrag vom 15. Dez 2009
Antwort Antwort
Benutzerbild von sundance
sundance

Registriert seit: 9. Mai 2006
98 Beiträge
 
Delphi 7 Professional
 
#1

Dateisuche - rekursiv

  Alt 8. Dez 2009, 13:22
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:
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;
Desweiteren bin ich auf die Komponente "DIFileFinder" gestoßen, die sehr interessant klingt:
Zitat:
Unlike most file finder components, DIFileFinder does not use events nor does it build lengthy file lists. Instead it reports a single file or folder at a time. This new architecture guarantees a maximum of flexibility using a minimum of resources only. Simply call the Next method each time another file entry is required. Next returns True for each file found, and the component can then be queried about the file details.
Dieser Ansatz hat mir sehr gut gefallen, denn man kann recht übersichtlich programmieren:
Delphi-Quellcode:
FFBasic.SearchFolder := edDirName.Text;
while FFBasic.Next do begin
  Memo1.Lines.Add(FFBasic.FullName);
end;
Mein Vorschlag für eine Next-Methode (ohne Filterung der Dateinamen) wäre dann so was:
Delphi-Quellcode:
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;
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?
Ich will hier auch nicht die €30 für die Komponente sparen; mich würde nur die (professionelle?) Lösung dieses Problems interessieren...

.sundance.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.033 Beiträge
 
Delphi 12 Athens
 
#2

Re: Dateisuche - rekursiv

  Alt 8. Dez 2009, 13:35
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 http://www.delphipraxis.net/internal...t.php?t=167783
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Benutzerbild von sundance
sundance

Registriert seit: 9. Mai 2006
98 Beiträge
 
Delphi 7 Professional
 
#3

Re: Dateisuche - rekursiv

  Alt 8. Dez 2009, 15:06
Zitat:
Du bist mir zuvorgekommen...
(ich wollte dich gerade nach einem iterativen Beispiel fragen, als ich bemerkt habe, dass das nicht unbedingt trivial ist )

Vielen Dank erstmal; ich werde das mal "durcharbeiten"...

.sundance.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.033 Beiträge
 
Delphi 12 Athens
 
#4

Re: Dateisuche - rekursiv

  Alt 8. Dez 2009, 15:12
Das Ganze komplett auf Callback umzustellen geht noch einfach.
Dieses ginge auch bei der rekursiven Lösung genauso einfach.

Delphi-Quellcode:
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;
Hier würde man die Funktion aufrufen und würde jede gefundene Datei sofort
im TFAFCallback geliefert bekommen.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.033 Beiträge
 
Delphi 12 Athens
 
#5

Re: Dateisuche - rekursiv

  Alt 8. Dez 2009, 17:23
http://www.delphipraxis.net/internal...t.php?t=167783

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.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Benutzerbild von sundance
sundance

Registriert seit: 9. Mai 2006
98 Beiträge
 
Delphi 7 Professional
 
#6

Re: Dateisuche - rekursiv

  Alt 10. Dez 2009, 10:52
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.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.033 Beiträge
 
Delphi 12 Athens
 
#7

Re: Dateisuche - rekursiv

  Alt 10. Dez 2009, 11:30
'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.

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.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Benutzerbild von sundance
sundance

Registriert seit: 9. Mai 2006
98 Beiträge
 
Delphi 7 Professional
 
#8

Re: Dateisuche - rekursiv

  Alt 15. Dez 2009, 11:37
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:
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;
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.

Sobald meine Klasse TFileFinder ganz fertig ist, werde ich das hier mal posten...

.sundance.
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 03:11 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz