Entwickler-Ecke

Dateizugriff - ...nach Dateien suchen mit Einschränkungen und Optionen


jaenicke - Fr 25.11.11 00:05
Titel: ...nach Dateien suchen mit Einschränkungen und Optionen
Hallo,

nach einer Frage im Forum habe ich einmal eine allgemeingültige Funktion zur Suche nach Dateien mit bestimmten Bedingungen erstellt. Diese ist explizit nicht für hohe Geschwindigkeit, sondern für viele Möglichkeiten gedacht. Dennoch ist die Geschwindigkeit denke ich hoch genug für die meisten Fälle.

Was ist möglich?Das ganze sieht dann so aus:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
{
  GetAllFilesEx by Sebastian Jänicke (jaenicke on delphi-forum.de) - 2011
  inspired by Popov: http://www.delphi-library.de/viewtopic.php?t=21275
  use as you want, but please don't remove this comment
}

type
  TFileSearchOptions = set of (fsoRecursive, fsoRelativePaths, fsoOnlyFilenames);
  TSearchFilesCallback = reference to procedure(const ACurrentPath: string);

function PathCombine(lpszDest: PWideChar; const lpszDir, lpszFile: PWideChar): PWideChar; stdcall;
  external 'shlwapi.dll' name 'PathCombineW';

procedure GetAllFilesEx(AFileList: TStrings; const AParentPath, ASearchPath, AMasks: stringconst AExcludes: string = '';
  ASearchOptions: TFileSearchOptions = [fsoRecursive]; AAttributes: Integer = faAnyFile; ACallback: TSearchFilesCallback = nil;
  AMinAge: TDateTime = 0; AMaxAge: TDateTime = 2 * MAXWORD);
var
  CompletePath, ParentPath, SearchPath, CurrentMask, CurrentFilename: string;
  Search: TSearchRec;
  MaskList, ExcludeList, ExcludedFiles: TStringList;
begin
  // combine paths to complete path
  if ASearchPath <> '' then
    SearchPath := IncludeTrailingPathDelimiter(ASearchPath)
  else
    SearchPath := '';
  if AParentPath <> '' then
    ParentPath := IncludeTrailingPathDelimiter(AParentPath)
  else
    ParentPath := '';
  SetLength(CompletePath, MAX_PATH);
  PathCombine(PChar(CompletePath), PChar(ParentPath), PChar(SearchPath));
  SetLength(CompletePath, StrLen(PChar(CompletePath)));

  // callback
  if Assigned(ACallback) then
    ACallback(CompletePath);

  // fill file list
  ExcludedFiles := TStringList.Create;
  try
    // find excluded files
    ExcludeList := TStringList.Create;
    try
      ExcludeList.StrictDelimiter := True;
      ExcludeList.CommaText := AExcludes;
      for CurrentMask in ExcludeList do
        if FindFirst(CompletePath + CurrentMask, AAttributes, Search) = 0 then
        begin
          repeat
            ExcludedFiles.Add(AnsiUpperCase(CompletePath + Search.Name));
          until FindNext(Search) <> 0;
          FindClose(Search);
        end;
    finally
      ExcludeList.Free;
    end;

    // find all matching files
    MaskList := TStringList.Create;
    try
      MaskList.StrictDelimiter := True;
      MaskList.CommaText := AMasks;
      for CurrentMask in MaskList do
        if FindFirst(CompletePath + CurrentMask, AAttributes, Search) = 0 then
        begin
          repeat
            CurrentFilename := CompletePath + Search.Name;
            if (ExcludedFiles.IndexOf(AnsiUpperCase(CurrentFilename)) < 0)
              and (Search.TimeStamp >= AMinAge) and (Search.TimeStamp <= AMaxAge) then
            begin
              if fsoRelativePaths in ASearchOptions then
                AFileList.Add(ExtractRelativePath(ParentPath, CurrentFilename))
              else if fsoOnlyFilenames in ASearchOptions then
                AFileList.Add(Search.Name)
              else
                AFileList.Add(CurrentFilename);
            end;
          until FindNext(Search) <> 0;
          FindClose(Search);
        end;
    finally
      MaskList.Free;
    end;
  finally
    ExcludedFiles.Free;
  end;

  // recurse into subfolders
  if fsoRecursive in ASearchOptions then
  begin
    if FindFirst(CompletePath + '*.*', faDirectory, Search) = 0 then
    begin
      repeat
        if ((Search.Attr and faDirectory) = faDirectory) and (Search.Name[1] <> '.'then
          GetAllFilesEx(AFileList, AParentPath, SearchPath + Search.Name, AMasks, AExcludes, ASearchOptions, AAttributes,
            ACallback, AMinAge, AMaxAge);
      until FindNext(Search) <> 0;
      FindClose(Search);
    end;
  end;
end;
Die Trennung in AParentPath und ASearchPath dient dazu, dass die Dateinamen relativ zu dem Hauptordner in AParentPath zurückgegeben werden können. Im zweiten Beispiel wird zwar unterhalb von C:\Program Files (x86)\Embarcadero\RAD Studio gesucht, aber "RAD Studio" ist in den Dateipfaden enthalten.

Ein kurzer Beispielaufruf, nur mit mehreren Masken:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
var
  FileList: TStringList;
begin
  FileList := TStringList.Create;
  try
    GetAllFilesEx(FileList, 'C:\Program Files (x86)\Embarcadero''''*.exe,*.txt');
    ShowMessage(FileList.Text);
  finally
    FileList.Free;
  end;
end;

Und ein ausführlicher Beispielaufruf sieht dann so aus, wobei der aktuelle Ordner als Status in die Caption des Formulars geschrieben wird:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
var
  FileList: TStringList;
begin
  FileList := TStringList.Create;
  try
    GetAllFilesEx(FileList, 'C:\Program Files (x86)\Embarcadero''RAD Studio''*.exe,*.txt''bds.exe,g*',
      [fsoRecursive, fsoRelativePaths], faAnyFile,
      procedure(const ACurrentPath: string)
      begin
        Caption := ACurrentPath;
      end);
    ShowMessage(FileList.Text);
  finally
    FileList.Free;
  end;
end;
Hier werden alle .exe oder .txt Dateien gesucht, wobei bds.exe und alle mit g beginnenden Dateien ausgeschlossen werden.

Die Funktion ist in der jetzigen Form ausschließlich mit Delphi XE und höher verwendbar. Für Delphi 2010 oder früher sind Anpassungen erforderlich (bei Delphi 2010 gibt es noch kein TimeStamp in TSearchRec und vor Delphi 2006 gibt es noch keine for..in Schleife). Ich denke aber der Sinn sollte klar werden, bei Bedarf könnt ihr euch das ja anpassen.

Weitere bereits vorhandene Varianten für eine Dateisuche von anderen Membern:
http://www.delphi-library.de/topic_nach+Dateien+mit+unterschiedlichen+Erweiterungen+suchen_21275.html
http://www.delphi-library.de/topic_rekursiv+nach+Dateien+in+Unterverzeichnissen+suchen_94.html
http://www.delphi-library.de/topic_+FindAllFiles+Dateisuche+mit+Fortschrittsanzeige_47880.html
http://www.delphi-library.de/topic_nach+Dateitypen+suchen+FindFirstFindNextVariante_15203.html
http://www.delphi-library.de/topic_nach+Dateien+suchen_1107.html

Viel Spaß damit ;-)
Schönen Gruß,
Sebastian