Delphi-PRAXiS
Seite 3 von 3     123   

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)

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 01:41 Uhr.
Seite 3 von 3     123   

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