AGB  ·  Datenschutz  ·  Impressum  







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

Dateiliste gefiltert erstellen

Ein Thema von DieDolly · begonnen am 24. Aug 2023 · letzter Beitrag vom 25. Aug 2023
Antwort Antwort
DieDolly

Registriert seit: 22. Jun 2018
2.175 Beiträge
 
#1

AW: Dateiliste gefiltert erstellen

  Alt 24. Aug 2023, 18:09
Anders erklärt.

Die Dateiliste A wird von Winapi.Windows.FindFirstFileEx() erstellt. Das sind alle Dateien in einem Verzeichnis.
Filterliste B sind ein paar Einträge von Dateien, die am Ende nicht in A landen sollen.

Das funktioniert ja alles schon, aber ist langsam. Weil jeder Fund von Winapi.Windows.FindFirstFileEx() iteriert die komplette Liste B durch.

Meine Idee wäre jetzt, die Dateiliste A erstmal ohne Filterung komplett zu erstellen und erst danach A und B zu vergleichen.

Alles aus B soll dann aus A verschwinden.

Fast vergessen. Da die Liste A Objekte enthält, muss ich da nicht zwangsläufig Einträge rauslöschen. Man könnte auch einfach eine Flag setzen, sodass die im späteren Verlauf ignoriert werden. Das würde der vorhandene Code zulassen.

Zusammengefasst:
beide Listen existieren, das Zusammenstellen der Listen ist rasendschnell und schnell genug.
Es hapert nur beim Vegleich.

Weil Wenn Liste A 15.000 Einträge hat und B 1000, sind das, wenn man jeden Eintrag mit jedem vergleicht, sehr viele Vergleiche, viele davon mehr als unnötig.

Geändert von DieDolly (24. Aug 2023 um 18:31 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

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

AW: Dateiliste gefiltert erstellen

  Alt 24. Aug 2023, 18:37
HashListe oder einfach nur eine sortierte Liste ... da lässt sich schneller finden,
selbst wenn man eine TStringList benutzt.

TStringList.Sorted=True und TStringList.IndexOf
TDictionary<>
TArray<string> mit TArray.BinarySearch<string> (zum Suchen, aber auch zum "sortierten" Einfügen), bzw. TArray.Sort<string>
...



EndsText kann ich nicht nehmen, weil ein FileX.exe in der Filterliste gilt nur im Hauptverzeichnis, nirgendwoanders.
Dann mußt du wohl deine Pfade zu RelativenPfaden machen.
und dann kannst'e mit MatchText oder TArray.BinarySearch oder

Delphi-Referenz durchsuchenExtractRelativePath
Delphi-Referenz durchsuchenTPath.IsPathRooted
siehe https://docwiki.embarcadero.com/RADS...ation_Routines
oder

oder ReplaceStr ähhh ReplaceText(FullFileName, 'C:\root\', '') [b][\b]
oder Delete(FullFileName, 1, Length('C:\root\')); (natrürlich aufpassen, falls nicht in diesem Pfad)
oder ...


https://stackoverflow.com/questions/...aths-in-delphi
Ein Therapeut entspricht 1024 Gigapeut.

Geändert von himitsu (24. Aug 2023 um 18:40 Uhr)
  Mit Zitat antworten Zitat
DieDolly

Registriert seit: 22. Jun 2018
2.175 Beiträge
 
#3

AW: Dateiliste gefiltert erstellen

  Alt 24. Aug 2023, 18:46
Da sind mir zu viele "oder" in dem Text. Zuviel Stress. Ich lasse alles so wie es ist

Geändert von DieDolly (24. Aug 2023 um 18:55 Uhr)
  Mit Zitat antworten Zitat
tomkupitz

Registriert seit: 26. Jan 2011
351 Beiträge
 
Delphi 12 Athens
 
#4

AW: Dateiliste gefiltert erstellen

  Alt 24. Aug 2023, 18:48
Zitat:
Weil Wenn Liste A 15.000 Einträge hat und B 1000, sind das, wenn man jeden Eintrag mit jedem vergleicht, sehr viele Vergleiche, viele davon mehr als unnötig.
Aber diese unnötigen Vergleiche verhinders du ja mit der HashListe.

vorhanden:

ListeB

File20
..
File30

von ListeB HashListe erstellen (oder gleich als Hash halten)


---

wird geade erstellt:

ListeA

File1 -> HashIndex berechnen und in ListeB im Index gucken => nicht da, kann in ListeA bleiben
..
File20 - HashIndex berechnen und in ListeB im Index gucken -> vorhanden, kann aus ListA raus
..
File100

ggf. Dateiname und Pfad wie oben erwähnt trennen und bei Vergleich nutzen.

Deine Suche dauert zu lange.
  Mit Zitat antworten Zitat
DieDolly

Registriert seit: 22. Jun 2018
2.175 Beiträge
 
#5

AW: Dateiliste gefiltert erstellen

  Alt 24. Aug 2023, 18:56
Ich kann nicht auf eine HashListe umstellen. Das geht einfach nicht mehr...

Wie sähe denn ein kleines Beispiel aus basierend auf Winapi.Windows.FindFirstFileEx()?

Vielleicht kann ich das anders lösen, ohne vorhandenen Code groß abändern zu müssen.
Meine Idee:
- Mit Winapi.Windows.FindFirstFileEx() Liste A erstellen und in HashListe packen.
- Liste B in HashListe packen (die steht in einer Textdatei).
Beide vergleichen.

Mit dem Rest von Liste A die Objekte erstellen.

Aber ich kann mir nicht vorstellen, wie das mit der HashListe funktioniert.

Meinst du sowas? https://www.delphipraxis.net/1159090-post3.html

Geändert von DieDolly (24. Aug 2023 um 19:04 Uhr)
  Mit Zitat antworten Zitat
tomkupitz

Registriert seit: 26. Jan 2011
351 Beiträge
 
Delphi 12 Athens
 
#6

AW: Dateiliste gefiltert erstellen

  Alt 24. Aug 2023, 19:43
Beispiel mit Kollisionsprüfung (verkettete Liste):

Code:
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  THashItem = class(TObject)
  public
    Key: string;

    Next: THashItem;
  end;

  THashList = class(TObject)
  private
    FItems: array of THashItem;

    function GetCount: Integer;
    function GetItem(Index: Integer): THashItem;
  public
    constructor Create(Count: Integer);
    destructor Destroy; override;

    function CreateHash(AKey: string): Integer;

    function Add(AKey: string): Boolean;
    procedure Clear;

    function Find(AKey: string): Integer;

    property Count: Integer read GetCount;
    property Items[Index: Integer]: THashItem read GetItem;
  end;

  TForm1 = class(TForm)
    ListBox1: TListBox;
    ListBox2: TListBox;
    Label1: TLabel;
    Label2: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

constructor THashList.Create(Count: Integer);
begin
  inherited Create;

  SetLength(FItems, Count);
end;

destructor THashList.Destroy;
begin
  Clear;

  Finalize(FItems);

  inherited Destroy;
end;

//

function THashList.GetCount: Integer;
begin
  result:=Length(FItems);
end;

function THashList.GetItem(Index: Integer): THashItem;
begin
  if (Index>-1) and (Index<Length(FItems)) then
    result:=FItems[Index]
  else
    result:=nil;
end;

//

function THashList.Add(AKey: string): Boolean;
var i: Integer;

    Item: THashItem;

begin
  i:=CreateHash(AKey);

  if (i>-1) and (i<Length(FItems)) then
  begin
    if FItems[i]=nil then
    begin
      FItems[i]:=THashItem.Create;
      FItems[i].Key:=AKey;
    end;

    Item:=FItems[i];

    while Item.Key<>AKey do
    begin
      if Item.Next=nil then
      begin
        Item.Next:=THashItem.Create;
        Item.Next.Key:=AKey;
      end;

      Item:=Item.Next;
    end;

    if Item.Key=AKey then
    begin
      result:=True;
      Exit;
    end
  end;

  result:=False;
end;

procedure THashList.Clear;
var i: Integer;

    Item, Next: THashItem;

begin
  for i:=0 to High(FItems) do
    if FItems[i]<>nil then
    begin
      Item:=FItems[i];

      while Item<>nil do
      begin
        Next:=Item.Next;

        Item.Free;

        Item:=Next;
      end;

      FItems[i]:=nil;
    end;
end;

//

function THashList.Find(AKey: string): Integer;
var i: Integer;

    Item: THashItem;

begin
  i:=CreateHash(AKey);

  if (i>-1) and (i<Length(FItems)) then
  begin
    Item:=FItems[i];

    while (Item<>nil) and (Item.Key<>AKey) do
      Item:=Item.Next;

    if (Item<>nil) and (Item.Key=AKey) then
    begin
      result:=i;
      Exit;
    end;
  end;

  result:=-1;
end;

//

function THashList.CreateHash(AKey: string): Integer;
var i: Integer;

begin
  result:=-1;

  if Length(AKey)=0 then
    Exit;

  result:=ord(AKey[1]) mod Length(FItems);

  for i:=2 to Length(AKey) do
    result:=(result*128+ord(AKey[i])) mod Length(FItems);
end;

//

var
  hl: THashList;

procedure TForm1.FormCreate(Sender: TObject);
var i: Integer;

begin
  hl:=THashList.Create(50);

  //ListeB

  for i:=20 to 30 do
    hl.Add('File'+IntToStr(i));

  //ListeA

  for i:=0 to 100 do
    if hl.Find('File'+IntToStr(i))=-1 then
      ListBox1.Items.Add('File'+IntToStr(i))
    else
      ListBox2.Items.Add('File'+IntToStr(i));
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  hl.Free;
end;

end.

###
object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 433
  ClientWidth = 622
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -12
  Font.Name = 'Segoe UI'
  Font.Style = []
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  TextHeight = 15
  object Label1: TLabel
    Left = 32
    Top = 24
    Width = 32
    Height = 15
    Caption = 'ListeA'
  end
  object Label2: TLabel
    Left = 247
    Top = 24
    Width = 31
    Height = 15
    Caption = 'ListeB'
  end
  object ListBox1: TListBox
    Left = 32
    Top = 56
    Width = 209
    Height = 329
    ItemHeight = 15
    TabOrder = 0
  end
  object ListBox2: TListBox
    Left = 247
    Top = 56
    Width = 209
    Height = 329
    ItemHeight = 15
    TabOrder = 1
  end
end
  Mit Zitat antworten Zitat
DieDolly

Registriert seit: 22. Jun 2018
2.175 Beiträge
 
#7

AW: Dateiliste gefiltert erstellen

  Alt 24. Aug 2023, 19:57
Ich habe das jetzt etwas anders gemacht. Mittels Winapi.Windows.FindFirstFileEx() hole ich mir alle Dateien und füge die in einen HashTable ein der so deklartiert ist
Delphi-Quellcode:
type
 TFileHashTable = TDictionary<string, TWin32FindData>;

var FileHashTable: TFileHashTable;

// hinzufügen
FileHashTable.Add(RootFolder + lfdStruct.cFileName, lfdStruct);
So hole ich Einträge die in Liste B sind aus Liste A raus
Delphi-Quellcode:
for i := 0 to ListeB.Count - 1 do
 begin
  if not FileHashTable.ContainsKey(RootFolder + ListeB.Strings[i]) then
   FileHashTable.Remove(RootFolder + ListeB.Strings[i]);
 end;
Aus dem was in FileHashTable jetzt übrig bleibt, möchte ich die Objekte erzeugen. Nur vielleicht stehe ich gerade auf dem Schlauch. Wie iteriere ich durch FileHashTable ?
  Mit Zitat antworten Zitat
tomkupitz

Registriert seit: 26. Jan 2011
351 Beiträge
 
Delphi 12 Athens
 
#8

AW: Dateiliste gefiltert erstellen

  Alt 24. Aug 2023, 20:27
Du nimmst erstmal alle auf und entfernst dann die aus ListeB. Besser ist Einträge aus ListeB erst gar nicht in ListeA aufzunehmen.

Du durchsuchst ja FileHashTable mit FileHashTable.ContainsKey, soweit ok. Aber ich weiß nicht wie (schnell) ContainsKey sucht.

Geändert von tomkupitz (24. Aug 2023 um 20:29 Uhr)
  Mit Zitat antworten Zitat
DieDolly

Registriert seit: 22. Jun 2018
2.175 Beiträge
 
#9

AW: Dateiliste gefiltert erstellen

  Alt 24. Aug 2023, 22:09
Zitat:
Du nimmst erstmal alle auf und entfernst dann die aus ListeB. Besser ist Einträge aus ListeB erst gar nicht in ListeA aufzunehmen.
So war es ja vorher und das war elendig langsam. Bei meinen Testdaten und mit dem alten Code hat das rund 4 Sekunden gedauert. Mit dem neuen Code jetzt nur noch wenn überhaupt 0,1 Sekunden.
Klar hat der neue Code nachteile. So kann ich keine Wildcards mehr verwenden, weil Strings 1zu1 abgeglichen werden. Aber besser der Code ist schnell.

Hier grob erklärt was ich jetzt mache

- Winapi.Windows.FindFirstFileEx() holt mir alle Dateien eines Verzeichnisses und fügt diese in meine Liste HashListe ein
type TFileHashTable = TDictionary<string, TWin32FindData>; var FileHashTable: TFileHashTable;

- ist das getan, gehe ich durch meine Liste B. Alles was in der fixen Liste B ist, wird aus der HashListe entfernt
Delphi-Quellcode:
for i := 0 to ListeB.Count - 1 do
 begin
  if FileHashTable.ContainsKey(RootFolder + ListeB.Strings[i]) then // ich bin mir nicht sicher, ob <Liste>.Remove selber nochmal prüft, ob der String vorhanden ist.
   FileHashTable.Remove(RootFolder + ListeB.Strings[i]);
 end;
- jetzt ist die HashListe bereinigt (alle Pfade aus Liste B sind jetzt nicht mehr dort drin)
- jetzt die Einträge der HashListe in meine eigentliche Liste A überführen, mit der ich weiterarbeite
Delphi-Quellcode:
for Key in FileHashTable.Keys do
 begin
  ...
 end;
Ja, Wildcards wären jetzt schön. Aber ich bin erstmal zufrieden.

Geändert von DieDolly (24. Aug 2023 um 22:16 Uhr)
  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 02:27 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz