Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Schnelle Methode, um eine Dateiliste zu erstellen (https://www.delphipraxis.net/207087-schnelle-methode-um-eine-dateiliste-zu-erstellen.html)

DieDolly 23. Feb 2021 11:59

Schnelle Methode, um eine Dateiliste zu erstellen
 
Vielleicht erst ein kleiner Hintergrund.
Ich habe früher mal anhand zweiter StringListen, die gefüllt sein können aber nicht müssen, eine weitere Liste erstellen lassen (eine Dateiliste).
Die eine StringListe enthielt (kann, muss nicht) Pfade, die unbedingt vom Such-Algorithmus (bei mir damals FindFirst, FindNext) in der neuen Dateiliste aufgenommen werden sollen. Die zweite StringListe enthielt Pfade, die in keinem Fall in der Dateiliste aufgenommen werden sollen.

Beispiele:
- war die erste StringListe gefüllt, wurden auch nur diese Pfade in der neuen Dateiliste aufgenommen.
- war die zweite gefüllt, wurden alle außer die in der Liste in die Dateiliste aufgenommen.

Basierend auf einem Verzeichnis "E:\": diese StringListen können beispielsweise enthalten
- Dateien
- Arbeit
- Freizeit\Fotos

Im o.g. Beispiel würden nur die Verzeichnisse "Dateien", "Arbeit" und "Freizeit\Fotos" in die neue Dateiliste übernommen (erste Liste)
ODER
alle außer genau die oben genannten (zweite Liste).


Meine vorgehensweise damals war in etwa so:
- FindFirst/FindNext-Procedure (in sich selbst aufrufend, wegen der Unterverzeichnisse)
-- jede Datei, jedes Verzeichnis das gefunden wird durch eine Funktion laufen lassen, die eine Schleife durchläuft und die StringListe prüft.
Und irgendwie habe ich beide Listen in das ganze Ding reingepackt mit if's und else's und viel zu kompliziert.


Wie realisiert man sowas mit D10.3.3 am besten? Ich habe leider keinen Code mehr davon. Ich weiß nur noch, dass das mit StringListen-Abgleich mindestens um den Faktor 10 langsamer war als ohne.

TurboMagic 23. Feb 2021 17:05

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Naja, du könntest mal anschauen was IOUtils so alles bietet.
Zum Beispiel TPath...

Uwe Raabe 23. Feb 2021 21:27

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Die Anforderungen sind aber noch nicht vollständig definiert, oder?

Was soll passieren, wenn beide Listen gefüllt sind?
Welche Liste hat Vorrang, wenn die Listen sich widersprechen?

DieDolly 23. Feb 2021 23:17

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Zitat:

Was soll passieren, wenn beide Listen gefüllt sind?
Beide Listen können nie zeitgleich gefüllt sein.

Ist die erste Liste gefüllt, wird die zweite gar nicht erst befüllt und kann ignoriert werden.
Wie das andersherum aussieht, muss ich noch überlegen.

Die erste Liste soll ausschließlich ihre Einträge vergleichen und Funde in eine Dateiliste schreiben.
Die zweite Liste soll ihre Einträge vergleichen und Funde sollen nicht zur Dateiliste hinzugefügt werden.

KodeZwerg 24. Feb 2021 00:33

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Das mit den verschiedenen Listen habe ich noch nicht so ganz verstanden, jedenfalls finde ich die schnellste Methode es über PItemIdList zu Lösen. Es ist gleichzeitig nicht die einfachste Möglichkeit da mehr Code benötigt wird.

In der Paxis kannst Du es testen indem Du mal TotalCommander (Shareware Datei-Manager) installierst und in ein Verzeichnis mit Tausenden von Dateien lotst.
Gegenüber FindFirst() ist das der Ferrari :)

In Kombination mit einem Verzeichnis-Monitor wirst du eventuell gar keine zwei Listen benötigen sondern einfach PIDLs sich selbst aktualisieren lassen.

Es gab mal eine VirtualShellView Demo von Borland um sich damit anzufreunden.

Vielleicht liege ich auch total daneben, dann tut es mir leid.

DieDolly 24. Feb 2021 07:25

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Zitat:

Zitat von KodeZwerg (Beitrag 1483653)
Das mit den verschiedenen Listen habe ich noch nicht so ganz verstanden

Halb-zitiert von mir:
Diese StringListen können beispielsweise nach einer Eingabe diesen Inhalt haben
- Dateien
- Arbeit
- Freizeit\Fotos

Wenn das da oben in der ersten Liste steht: nur die Verzeichnisse "Dateien", "Arbeit" und "Freizeit\Fotos" würden in die neue Dateiliste übernommen
ODER
wenn das in der zweiten Liste streht: alle außer genau die oben genannten werden in die neu Dateiliste übernommen.

Also quasi "Füge nur diese Dateien (wenn gefunden) in die Dateiliste ein (erste Liste)
oder
füge alle Dateien außer die oben (wenn gefunden) in die Dateiliste ein (zweite Liste).

Blup 24. Feb 2021 09:22

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Man benötigt eigentlich immer zumindest ein zu durchsuchendes Verzeichnis.
Deshalb würde ich dieses in der Liste der zu durchsuchenden Verzeichnisse übergeben.
Die Verzeichnisse, die von der Suche ausgeschlossen sind, sollten aber dann höhere Priorität haben.
Bsp. Wenn "c:\" dursucht werden soll und "c:\temp" ausgeschlossen ist.

Ungetestet:
Delphi-Quellcode:
procedure FindFiles(ADirList: TStrings; const AFileMask: string; AExcludeDirList: TStrings; AFileList: TStrings);
var
  SubDirList: TStringList;
  FindInfo: TSearchRec;
  sDir: string;
begin
  SubDirList := TStringList.Create;
  try
    for sDir in ADirList do
    begin
      if AExcludeDirList.IndexOf(sDir) < 0 then
      begin
        if FindFirst(sDir + AFileMask, faAnyFile, FindInfo); then
        begin
          try
            repeat
              if (FindInfo.Attr and faDirectory) = faDirectory then
 
              begin
                if (FindInfo.Name <> '.') and (FindInfo.Name <> '..') then
                  SubDirList.Add(sDir + FindInfo.Name + '\');

              end
              else
                AFileList.Add(sDir + FindInfo.Name);
            until not FindNext(FindInfo);
          finally
            FindClose(FindInfo);
          end;
        end;

        if SubDirList.Count > 0 then
        begin
          FindFiles(SubDirList, AFileMask, AExcludeDirList, AFileList);  
          SubDirList.Clear;
        end;
      end;
    end;
  finally
    SubDirList.Free;
  end;
end;

DieDolly 24. Feb 2021 09:31

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Zitat:

Deshalb würde ich dieses in der Liste der zu durchsuchenden Verzeichnisse übergeben.
Verstehe diesen Satz nicht. Das zu durchsuchende Verzeichnis darf nicht in einer der beiden Listen vorkommen.
Die beiden Listen dürfen nur Unterverzeichnisse dieses Verzeichnisses beinhalten.

Jumpy 24. Feb 2021 09:50

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Keine Ahnung ob das schneller ist, aber könnte man nicht erst nur nach Verzeichnissen suchen, die den Kriterien entsprechen und diese in eine Liste schreiben und dann anschließen in allen Verzeichnissen in dieser Liste die Dateien suchen?

Blup 24. Feb 2021 10:09

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Zitat:

Zitat von DieDolly (Beitrag 1483668)
Zitat:

Deshalb würde ich dieses in der Liste der zu durchsuchenden Verzeichnisse übergeben.
Verstehe diesen Satz nicht. Das zu durchsuchende Verzeichnis darf nicht in einer der beiden Listen vorkommen.
Die beiden Listen dürfen nur Unterverzeichnisse dieses Verzeichnisses beinhalten.

Nach deiner Beschreibung gibt es zwei Anwendungsfälle:
1: Eine List von Verzeichnissen soll durchsuncht werden, einschliesslich Unterverzeichnisse.
Delphi-Quellcode:
procedure Fall1(ASuchList: TStrings; const AFileMask: string; AFileList: TStrings);
var
  EmptyList: TStringList;
begin
  EmptyList := TStringList.Create;
  try
    FindFiles(ADirList, AFileMask, EmptyList, AFileList);
  finally
    EmptyList.Free;
  end;
end;
2: Ein Verzeichnis soll durchsucht werden, einschliesslich Unterverzeichniss. Bestimmte Unterverzeichnisse sollen komplett ausgeschlossen werden.
Delphi-Quellcode:
procedure Fall2(const ASuchDir: string; const AFileMask: string; AExcludeDirList: TStrings; AFileList: TStrings);
var
  DirList: TStringList;
begin
  DirList := TStringList.Create;
  try
    DirList.Add(ASuchDir);
    FindFiles(DirList, AFileMask, EmptyList, AFileList);
  finally
    DirList.Free;
  end;
end;

Blup 24. Feb 2021 10:10

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Zitat:

Zitat von Jumpy (Beitrag 1483669)
Keine Ahnung ob das schneller ist, aber könnte man nicht erst nur nach Verzeichnissen suchen, die den Kriterien entsprechen und diese in eine Liste schreiben und dann anschließen in allen Verzeichnissen in dieser Liste die Dateien suchen?

Ist auf jeden Fall langsamer, da jedes Verzeichnis zwei mal durchsuncht wird.

Uwe Raabe 24. Feb 2021 10:14

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Ich bin mir nicht sicher, ob ich das richtig verstanden habe, aber hier mal ein Entwurf mit Verwendung der System.IOUtils. Erstmal ungetestet und ohne Berücksichtigung der Performance.

Delphi-Quellcode:
procedure ListFiles(const Root: string; LstInclude, LstExclude, Target: TStrings);
var
  path: string;
  S: string;
  skip: Boolean;
begin
  if (LstInclude <> nil) and (LstInclude.Count > 0) then begin { Includes vorhanden? }
    for path in LstInclude do
      Target.AddStrings(TDirectory.GetFiles(TPath.Combine(Root, path), '*.*', TSearchOption.soAllDirectories));
  end
  else if (LstExclude <> nil) and (LstExclude.Count > 0) then begin { Excludes vorhanden? }
    for path in TDirectory.GetDirectories(Root, '*', TSearchOption.soAllDirectories) do begin
      { ist es ein Exclude Path? }
      skip := False;
      for S in LstExclude do
        if path.StartsWith(TPath.Combine(Root, S), True) then begin
          skip := True;
          Break;
        end;
      if not skip then
        Target.AddStrings(TDirectory.GetFiles(path, '*.*', TSearchOption.soTopDirectoryOnly));
    end;
  end
  else begin { keine Einschränkungen }
    Target.AddStrings(TDirectory.GetFiles(Root, '*.*', TSearchOption.soAllDirectories));
  end;
end;

DieDolly 24. Feb 2021 10:26

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Ich teste das gleich mal durch. Auf den ersten Blick kann man nur Pfade in die Listen hinzufügen.
Was muss ich abändern, damit man auch Dateien einfügen kann?

Die Listen können Datei-Pfade enthalten.

Wenn ich das Root E:\ habe und in E der Ordner Dateien ist, dort drin eine Datei Test.txt, diese möchte ich gerne ein- oder ausschließen können, den Rest der Dateien in diesem Ordner aber nicht.
Zeitgleich natürlich auch ganze Verzeichnisse so wie es jetzt schon ist.

Ist das hier ok?
Delphi-Quellcode:
function IsDirectory(const aFileName: string): Boolean;
var
 R: DWORD;
begin
 R := GetFileAttributes(PChar(aFileName));
 Result := (R <> DWORD(-1)) and ((R and FILE_ATTRIBUTE_DIRECTORY) <> 0);
end;

   for path in LstInclude do
    begin
     if IsDirectory(TPath.Combine(Root, path)) then
      Target.AddStrings(TDirectory.GetFiles(TPath.Combine(Root, path), '*.*', TSearchOption.soAllDirectories))
     else
      Target.Add(TPath.Combine(Root, path));
    end;

Andreas13 24. Feb 2021 10:35

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Hallo Uwe,
In 3 Zeilen
Delphi-Quellcode:
Target.AddStrings(TDirectory.GetFiles(TPath.Combine(Root, path), '*.*', TSearchOption.soAllDirectories));
..
Target.AddStrings(TDirectory.GetFiles(path, '*.*', TSearchOption.soTopDirectoryOnly));
..
Target.AddStrings(TDirectory.GetFiles(Root, '*.*', TSearchOption.soAllDirectories));
gibt XE5 kommt folgende Fehlermeldung:
[dcc32 Fehler] PDBiA_System.pas(1309): E2250 Es gibt keine überladene Version von 'AddStrings', die man mit diesen Argumenten aufrufen kann

Was müßte ich an Deinem Code ändern, damit es auch bei XE5 funktioniert?
Danke im Voraus!
Gruß, Andreas

Uwe Raabe 24. Feb 2021 10:42

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Zitat:

Zitat von Andreas13 (Beitrag 1483677)
Was müßte ich an Deinem Code ändern, damit es auch bei XE5 funktioniert?

Z.B. einen Class-Helper schreiben, der ein AddStrings für ein string-Array unterstützt.

DieDolly 24. Feb 2021 10:46

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Bei LstExclude bleibt Target leider leer. Wenn ich Dateien\Test.txt in LstExclude hinzufüge, bleibt die Liste komplett leer.
Änderre ich TDirectory.GetDirectories zu TDirectory.GetFiles ab, funktioniert es. Aber ist das noch so gewollt?

Hier meine Änderungen, damit auch Dateien funktionieren
Delphi-Quellcode:
function IsDirectory(const aFileName: string): Boolean;
var
 R: DWORD;
begin
 R := GetFileAttributes(PChar(aFileName));
 Result := (R <> DWORD(-1)) and ((R and FILE_ATTRIBUTE_DIRECTORY) <> 0);
end;

procedure ListFiles(const Root: string; LstInclude, LstExclude, Target: TStrings);
var
 path: string;
 S: string;
 skip: Boolean;
begin
 if (LstInclude <> nil) and (LstInclude.Count > 0) then
  begin {Includes vorhanden?}
   for path in LstInclude do
    begin
     if IsDirectory(TPath.Combine(Root, path)) then
      Target.AddStrings(TDirectory.GetFiles(TPath.Combine(Root, path), '*.*', TSearchOption.soAllDirectories))
     else
      Target.Add(TPath.Combine(Root, path));
    end;
  end
 else if (LstExclude <> nil) and (LstExclude.Count > 0) then
  begin {Excludes vorhanden?}
   for path in TDirectory.GetFiles(Root, '*.*', TSearchOption.soAllDirectories) do
    begin
     {ist es ein Exclude Path?}

     for S in LstExclude do
      begin
       skip := False;
       if path.StartsWith(TPath.Combine(Root, S), True) then
        begin
         skip := True;
         Break;
        end;
      end;

     if not skip then
      begin
       if IsDirectory(TPath.Combine(Root, path)) then
        Target.AddStrings(TDirectory.GetFiles(path, '*.*', TSearchOption.soTopDirectoryOnly))
       else
        Target.Add(TPath.Combine(Root, path));
      end;
    end;
  end
 else
  begin {keine Einschränkungen}
   Target.AddStrings(TDirectory.GetFiles(Root, '*.*', TSearchOption.soAllDirectories));
  end;
end;

Uwe Raabe 24. Feb 2021 11:30

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Die Anforderung, dass die Listen auch Dateinamen (oder womöglich auch Wildcards) enthalten können, macht einen geänderten Ansatz notwendig. Deine Änderungen weichen zu stark von meinem Vorschlag ab, als dass das noch funktionieren würde.

DieDolly 24. Feb 2021 11:45

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Schade :( dann weiß ich leider nicht weiter als mit FindFirst und FindNext, was aber wieder langsam ist wenn ich das mache weil ich alle Dateien durchgehe.

Schade weil... ich habe noch ein altes Programm rumliegen (nur die ausführbare Datei) wo so ein alter Code von mir angewendet wird.
Bei meiner Festplatte P:\ (portable programme, 66 Verzeichnisse, 1 soll in die LstInclude-List) dauert das 10 Sekunden.
Mit deinem Code und meinen (schlechten) Änderungen, 12 Millisekunden!

jaenicke 24. Feb 2021 12:31

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Die schnellste Variante wäre es so zu machen wie Everything und andere Tools:
Direkt auf die NTFS Datenbank zugreifen.

Damit kann man innerhalb von Millisekunden selbst Partitionen mit vielen Dateien durchsuchen.
https://blogs.msmvps.com/bsonnino/20...fs-structures/

Leider braucht man dafür Adminrechte und es ist auch nicht so einfach.

DieDolly 24. Feb 2021 12:43

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Adminrechte sind ausgeschlossen, da ich Zugriff auf Netzlaufwerke brauche.
Uwe's Lösung ist schon wahnsinnig schnell, jetzt fehlen nur noch Wildcards. Dateien hab ich schon ergänzt.
Für Wildcards kenne ich nur PathMatchSpecW.

Uwe Raabe 24. Feb 2021 12:46

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Ich sage ja nicht, dass es nicht geht. Es gibt halt noch Lücken bei der Spezifikation bzw. man muss sich einige aus den diversen Posts hier erst zusammensuchen. Konkrete Beispiele wären sicher auch hilfreich.

Ohne Anspruch auf Vollständigkeit:

Kann LstInclude außer relativen Pfaden auch Dateinamen enthalten?
Wenn ja, haben die immer einen Pfad davor oder gelten die für alle Pfade? (z.B: alle readme.txt in allen durchsuchten Verzeichnissen)
Sind Wildcards erlaubt?
Kann LstInclude bzw. LstExclude auch absolute Pfade und/oder Dateinamen enthalten?
Wenn ja, können die von Root abweichen?
Was ist, wenn sich LstInclude und LstExclude widersprechen?

Ich könnte mir eine ganze Reihe von Test-Cases vorstellen, um die unterschiedlichen Fälle abzudecken.

DieDolly 24. Feb 2021 13:02

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Zitat:

Kann LstInclude außer relativen Pfaden auch Dateinamen enthalten?
Wenn ja, haben die immer einen Pfad davor oder gelten die für alle Pfade? (z.B: alle readme.txt in allen durchsuchten Verzeichnissen)
Sind Wildcards erlaubt?
Kann LstInclude bzw. LstExclude auch absolute Pfade und/oder Dateinamen enthalten?
Wenn ja, können die von Root abweichen?
Was ist, wenn sich LstInclude und LstExclude widersprechen?
1) das können Verzeichnisnamen und Dateinamen sein, richtig
2) es ist nie ein Pfad davor bzw. es sollte keiner davor sein
3) Wildcards * und ?, beide werden von PathMatchSpecW unterstützt. Datei.* , *.txt , Dat??.txt usw.
4) siehe 2. Der Basispfad darf nicht dabei sein
5) nein
6) wenn LstInclude Count>0 ist, wird LstExclude ignoriert.

Was widersprecht denn der Nutzung von deinem Code ListFiles() mit meinen Änderungen außer, dass keine Wildcards funktionien?

Uwe Raabe 24. Feb 2021 13:13

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Wenn ich also mal das Beispiel deines Eingangsposts nehme:

Zitat:

Basierend auf einem Verzeichnis "E:\": diese StringListen können beispielsweise enthalten
- Dateien
- Arbeit
- Freizeit\Fotos
Dann könnte man die Liste so aussehen:
Zitat:

Dateien
Arbeit
Freizeit\Fotos
*.jpg
*.bmp
und das würde dann alle jpg- und bmp-Dateien in den drei genannten Verzeichnissen samt ihrer Unterverzeichnisse aufgelistet?

DieDolly 24. Feb 2021 13:17

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Das ist jetzt eine gute Frage.
Vielleicht eher so, damit die Wildcards ans Verzeichnis gebunden sind

Zitat:

Dateien
Arbeit
Freizeit\Fotos\*.jpg
Dateien, Arbeit: das komplette Verzeichnis wird verarbeitet
Freizeit\Fotos\*.jpg: nur jpg-Dateien in Freizeit\Fotos.

So könnte das auch aussehen (Dateien: nur pas-Dateien, Arbeit: komplett, Freizeit\Fotos komplett)
Zitat:

Dateien\*.pas
Arbeit
Freizeit\Fotos
Oder so (Dateien, Arbeit und Freizeit\Fotos: komplett)
Zitat:

Dateien
Arbeit
Freizeit\Fotos

Oder so (Dateien, Arbeit und Freizeit\Fotos: das würde mit PathMatchSpecW nur Dateien durchlassen, die mit "img" anfangen, danach "ein beliebiger Buchstabe oder Zahl")
Zitat:

Dateien
Arbeit
Freizeit\Fotos\img?.txt

himitsu 24. Feb 2021 13:34

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
TMask oder Delphi-Referenz durchsuchenMatchesMask (System.Masks)

oder die Maske in einen RegEx konvertieren (so in etwa):
$ -> \$
^ und $ davor/dahinter
.*$ -> (nichts)
.$ -> $
. -> \.
? -> . oder [^\\]
* -> .* oder [^\\]*


Die beiden mit . und $, weil Punkt am Ende optional ist, also ignoriert wird.

DieDolly 24. Feb 2021 13:37

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Ich habe nicht einmal hinbekommen PathMatchSpecW in Uwe's Code (der für Dateien und Verzeichnisse perfekt funktioniert) einzubauen. Das da ist ja noch komplizierter :D

Ich benutze auch noch MustangpeakVirtualshellTools das kann eh keine Wildcards. Von daher ist es eigentlich egal.
Die entwickler scheinen auch keine Lust zu haben das einzubauen.

Uwe Raabe 24. Feb 2021 13:59

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Bei dem Exclude-Fall müsste man unterscheiden, ob lediglich ein Verzeichnis angegeben ist oder ein Verzeichnis mit Wildcards. Im ersten Fall kann man das Verzeichnis überspringen (das macht mein erster Vorschlag), aber im zweiten Fall muss das Verzeichnis gescannt und die entsprechenden Einträge ausgefiltert werden.

Um das besser kontrollieren zu können, würde ich die LstExclude in zwei Listen LstExcludeDirs und LstExcludeFiles aufsplitten. Hier nochmal ein etwas komplexerer Ansatz (mit kleiner Optimierung beim Checken der Excludes) - ungetestet:
Delphi-Quellcode:
procedure ListFiles(const Root: string; LstInclude, LstExclude, Target: TStrings);

  function Matches(const Value: String; const Arr: TArray<string>): Boolean;
  var
    foundIndex: Integer;
  begin
    Result := TArray.BinarySearch<string>(Arr, Value, foundIndex, TIStringComparer.Ordinal);
    if not Result and (foundIndex > 0) then
      Result := Value.StartsWith(Arr[foundIndex-1], True);
  end;

var
  arr: TArray<string>;
  lstExcludeDirs: TStringList;
  lstExcludeFiles: TStringList;
  mask: string;
  path: string;
  S: string;
begin
  if (LstInclude <> nil) and (LstInclude.Count > 0) then begin { Includes vorhanden? }
    for S in LstInclude do begin
      path := TPath.Combine(Root, S);
      mask := '*.*';
      if path.Contains('*') or path.Contains('?') then begin
        mask := TPath.GetFileName(path);
        path := TPath.GetDirectoryName(path);
      end;
      Target.AddStrings(TDirectory.GetFiles(path, mask, TSearchOption.soAllDirectories));
    end;
  end
  else if (LstExclude <> nil) and (LstExclude.Count > 0) then begin { Excludes vorhanden? }
    lstExcludeDirs := TStringList.Create;
    try
      lstExcludeFiles := TStringList.Create;
      try
        for S in LstExclude do begin
          path := S;
          if path.Contains('*') or path.Contains('?') then begin
            LstExcludeFiles.Add(path);
            { damit der Pfad erstmal excluded wird, tragen wir ihn auch in die andere Liste ein }
            path := TPath.GetDirectoryName(path);
          end;
          LstExcludeDirs.Add(path);
        end;

        { Erst arbeiten wir die ganzen Pfade ab und überspringen die Excluded Pfade }
        { Für bessere Performance sortieren wir die Excludes einmal, damit wir in Matches das BinarySearch verwenden können }
        arr := LstExcludeDirs.ToStringArray;
        TArray.Sort<string>(arr, TIStringComparer.Ordinal);

        for path in TDirectory.GetDirectories(Root, '*', TSearchOption.soAllDirectories) do begin
          { ist es ein Exclude Path? }
          if not Matches(path, arr) then
            Target.AddStrings(TDirectory.GetFiles(path, '*.*', TSearchOption.soTopDirectoryOnly));
        end;

        { nun noch die excluded Paths mit den Wildcard-Einträgen }
        for S in lstExcludeFiles do begin
          mask := TPath.GetFileName(S);
          path := TPath.GetDirectoryName(S);
          Target.AddStrings(TDirectory.GetFiles(path, '*.*', TSearchOption.soAllDirectories,
            function(const Path: string; const SearchRec: TSearchRec): Boolean
            begin
              Result := TPath.MatchesPattern(SearchRec.Name, mask, False);
            end
            ));
        end;
      finally
        lstExcludeFiles.Free;
      end;
    finally
      lstExcludeDirs.Free;
    end;
  end
  else begin { keine Einschränkungen }
    Target.AddStrings(TDirectory.GetFiles(Root, '*.*', TSearchOption.soAllDirectories));
  end;
end;

DieDolly 24. Feb 2021 14:25

AW: Schnelle Methode, um eine Dateiliste zu erstellen
 
Ist TDirectory.GetFiles und TPath das moderne FindFirst FindNext?
Ich habe um alle aufrufe Target.AddStrings noch ein TDirectory.Exists(path) gesetzt, damit es nicht zu Fehlern kommt wenn es da was nicht gibt (Garbage in, Garbage out: falsche Parameter die man übergibt oder so).

Meine testfälle scheinen alle zu funktionieren, was LstInclude angeht. LstExlude muss ich noch testen aber ich denke, das funktioniert alles.

Ok einen Fall hab ich das funktioniert nicht. Man kann keine Dateien mehr in die Listen schreiben.
Deswegen habe ich etwas umgeändert
Delphi-Quellcode:
// statt
Target.AddStrings(TDirectory.GetFiles(path, mask, SearchOption));

// jetzt
     if IsDirectory(path) then
      begin
       if TDirectory.Exists(path) then
        Target.AddStrings(TDirectory.GetFiles(path, mask, SearchOption));
      end
     else
      Target.Add(path);
Aber irgendwas stimmt auch nicht. Deswegen habe ich noch weiter rumprobiert ohne zu wissen was ich wirklich mache. Aber jetzt scheint es zu funktionieren mit beiden Listen.
EINE Sache funktioniert aber noch immer nicht.

LstInclude.Add('*.txt');
ListFiles('P:\Audacity Portable', LstInclude, LstExclude, sl, True);

Hier denke ich, dass nur Txt-Dateien i, Root von ListFiles eingeschlossen werden sollen. Es werden aber alle in allen Unterverzeichnissen übernommen.
Ich probiere noch weiter rum irgendwann werde ich eine Lösung haben.
Bis ich die habe, habe ich den Code hier unten erstmal wieder gelöscht.


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:21 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