Home » Tipps & Tricks » Dateien/Verzeichnisse » Dateioperationen » Rekursiv nach Dateien suchen

Rekursiv nach Dateien suchen

Um nach bestimmten Dateien auf der Festplatte zu suchen muss man die Funktionen FindFirst und FindNext benutzen. Wenn man nur nach Dateien in einem Verzeichnis suchen möchte ist dies sehr einfach. Man sucht einfach mit FindFirst nach der ersten Datei und startet dann eine Repeat-Schleife. Am Ende eines jeden Durchlaufs wird die nächste Datei in dem Verzeichnis gesucht (FindNext).

Etwas komplizierter wird die Sache, wenn man auch in Unterverzeichnissen nach Dateien suchen möchte. Dann muss sich die Funktion nämlich selbst aufrufen – immer genau dann, wenn ein Verzeichnis gefunden wurde. Dies nennt man rekursiv.
In der untenstehenden Procedure wurde der gesamte Aufruf gekapselt.

Die Procedure erwartet fünf Parameter:

  • Das Verzeichnis (z.B. ‚C:\Programme‘)
  • Die Suchmaske (z.B. ‚*.*‘ oder ‚*.doc‘)
  • Die Ausgabeliste (z.B. Listbox1.Items)
  • Rekursive Suche (bei True werden die Verzeichnisse rekursiv durchsucht, also mitsamt allen Unterverzeichnissen, bei False wird nur das angegebene Verzeichnis durchsucht).
  • Ob die Ausgabeliste vorher gelöscht werden soll, bei True werden nur die neuen Suchergebnisse zurückgegeben, bei False bleiben bisherige Einträge erhalten.
procedure GetFilesInDirectory(Directory: string; const Mask: string;
                              List: TStrings;
                              WithSubDirs, ClearList: Boolean);

  procedure ScanDir(const Directory: string);
  var
    SR: TSearchRec;
  begin
    if FindFirst(Directory + Mask, faAnyFile and not faDirectory, SR) = 0 then try
      repeat
        List.Add(Directory + SR.Name)
      until FindNext(SR) <> 0;
    finally
      FindClose(SR);
    end;
  
    if WithSubDirs then begin
      if FindFirst(Directory + '*.*', faAnyFile, SR) = 0 then try
        repeat
          if ((SR.attr and faDirectory) = faDirectory) and
             (SR.Name <> '.') and (SR.Name <> '..') then
            ScanDir(Directory + SR.Name + '\');
        until FindNext(SR) <> 0;
      finally
        FindClose(SR);
      end;
    end;
  end;

begin
  List.BeginUpdate;
  try
    if ClearList then
      List.Clear;
    if Directory = '\' then Exit;
    if Directory[Length(Directory)] <> '\' then
      Directory := Directory + '\';
    ScanDir(Directory);
  finally
    List.EndUpdate;
  end;
end;

Ein Beispielaufruf könnte so aussehen:

procedure TForm1.Button1Click(Sender: TObject);
begin
  GetFilesInDirectory('C:\
', '*.*', Listbox1.Items, False, True);
end;

In diesem Beispiel werden alle Dateien (*.*) im Verzeichnis ‚C:‘ in ‚Listbox1.Items‘ gesteckt. Unterverzeichnisse werden nicht durchsucht (False).

FindFirst

Das Kernstück dieser Procedure ist eigentlich der Aufruf von FindFirst. Diese Funktion sucht nach der ersten Datei im angegebenen Verzeichnis. Sie erwartet drei Parameter:

  • Pfad + Suchmaske
  • Dateiattribut
  • Referenzparameter vom Typ TsearchRec

Pfad und Suchmaske sollten klar sein (z.B. C:\*.*). Der zweite Parameter kann folgende Werte annehmen:

faReadOnly      Schreibgeschützte Datei
faHidden        Versteckte Datei
faSysFile       Systemdatei
faVolumeID      Laufwerks-ID-Datei
faDirectory     Verzeichnis
faArchive       Archivdatei
faAnyFile       Beliebige Datei

Der Parameter gibt an, welche Dateien mit welchem Dateiattribut gesucht werden sollen. Auch kann auf diese Weise nach Verzeichnissen gesucht werden können.

Dem dritten Parameter werden letztendlich bei einem Sucherfolg die Dateiinformationen übergeben:

type
  TSearchRec = record   
    Time: Integer;   
    Size: Integer;   
    Attr: Integer;   
    Name: TFileName;   
    ExcludeAttr: Integer;
    FindHandle: THandle;
    FindData: TWin32FindData;
  end;

Auf diese Weise kommt man auch an den eigentlichen Dateinamen, die Dateigröße oder an das Erstellungsdatum:

procedure TForm1.Button1Click(Sender: Tobject);
var
  F: TSearchRec;
begin
  if FindFirst(ed_dateiname.text, faAnyFile, F) = 0 then try
    Label1.Caption := F.Name
    Label2.Caption := IntToStr(F.Size);
  finally
    FindClose(F);
  end;
end;

FindNext setzt einfach nur das Filehandle auf die nächste entsprechende Datei. Wieder muss die Variable vom Typ TSearchRec als Referenzparameter übergeben werden.

Da beim Aufruf von FindFirst usw. Ressourcen belegt werden, müssen sie wieder freigegeben werden. Dies geschieht mit dem Aufruf von FindClose.

4 Gedanken zu „Rekursiv nach Dateien suchen“

  1. der Code funktioniert so nicht. Es fehlen an drei Stellen der Backslah und an einer Stelle ein Ungleich-Zeichen.

    ist:
    ScanDir(Directory + SR.Name + “);
    if Directory[Length(Directory)] “ then
    Directory := Directory + “;

    soll:
    ScanDir(Directory + SR.Name + ‚\‘);
    if Directory[Length(Directory)] <> ‚\‘ then
    Directory := Directory + ‚\‘;

  2. Sie haben die Ergänzung in der ersten Zeile vergessen (im WithSubDirs-Block):

    ScanDir(Directory + SR.Name + “);

    muss heißen:

    ScanDir(Directory + SR.Name + ‚\‘);

     

    Ansonsten wird das nächste Verzeichnis ohne abschließenden Backslash durchsucht was z.B. C:\temp\temptemp mit Verzeichnisausgang C:\temp\temp zur Folge hat 🙂

     

     

Kommentare sind geschlossen.