Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Dateinamen auslesen und sortieren (https://www.delphipraxis.net/212811-dateinamen-auslesen-und-sortieren.html)

Nico B. 5. Apr 2023 11:34

Delphi-Version: XE7

Dateinamen auslesen und sortieren
 
Hallo zusammen,

ich finde einfach keine "einfache" Lösung für folgendes Problem.

Ich möchte Dateinamen aus einem Ordner auslesen und sortieren.
Dazu habe ich einen Ordner mit CSV-Dateien.
Folgende Dateien sind enthalten:
  • 1.3.csv
  • 1.6.csv
  • 1.7.csv
  • 2.3.csv
  • 2.6.1.csv
  • 2.6.2.csv
  • 2.6.3.csv
  • 2.8.csv
  • 2.12.csv
  • 4.5.csv
  • 4.8.csv
  • 4.9.1.csv
  • 4.9.2.csv
  • ....
Genau in dieser Reihenfolge werden mir die Dateien vom Windows-Explorer angezeigt.
Und genau in dieser Reihenfolge möchte ich die Dateien gern weiterverwenden.

Ich habe nun versucht die Dateinamen einzulesen und per Add in eine TStringList zu schreiben.
Dann rutscht die Datei 2.12.csv aber vor die 2.3.csv.
Also habe ich die TStringList.Sorted auf True gesetzt.
Das Ergebnis bleibt das gleiche.

Ich habe mich jetzt auch schon mit TStringList.CustomSort beschäftigt. Da steige ich aber nicht so richtig dahinter.
Außerdem scheint mir das auch relativ aufwendig.

Das Ergebnis muss auch nicht zwangsläufig in eine TStringList. Ich will eben nur die Dateien in der gleichen Reihenfolge verarbeiten wie sie mir im Windows-Explorer angezeigt werden.
Hat jemand eine Idee wie das geht?

DeddyH 5. Apr 2023 11:52

AW: Dateinamen auslesen und sortieren
 
Ist hier etwas für Dich dabei? https://www.delphipraxis.net/211914-...tringwert.html

Nico B. 5. Apr 2023 13:54

AW: Dateinamen auslesen und sortieren
 
Ich habe es wie beschrieben mit der Funktion in Verbindung mit CustomSort versucht.
Delphi-Quellcode:
CompareString(LOCALE_USER_DEFAULT, SORT_STRINGSORT or SORT_DIGITSASNUMBERS, PChar(S1),
    Length(S1), PChar(S2), Length(S2)) - CSTR_EQUAL;
Leider bleibt das Ergebnis das gleiche.

KodeZwerg 5. Apr 2023 14:10

AW: Dateinamen auslesen und sortieren
 
Habe es nur mal rasch mit FreePascal probiert und stelle nichts falsches fest wenn ich es so mache:
Code:
program Project1;

uses
  SysUtils, Classes;

function CompareProc(List: TStringList; Index1, Index2: Integer): Integer;
begin
  Result := string.Compare(List[Index1], List[Index2], [coIgnoreCase]);
end;

var
  SL: TStringList;
  i: Integer;
begin
  SL := TStringList.Create;
  try
    SL.Add('2.6.1.csv');
    SL.Add('4.5.csv');
    SL.Add('4.9.1.csv');
    SL.Add('1.3.csv');
    SL.Add('2.3.csv');
    SL.Add('2.6.2.csv');
    SL.Add('1.7.csv');
    SL.Add('4.9.2.csv');
    SL.Add('1.6.csv');
    SL.Add('2.8.csv');
    SL.Add('2.6.3.csv');
    SL.Add('4.8.csv');
    SL.Add('2.12.csv');
    SL.CustomSort(@CompareProc);
    for i := 0 to Pred(SL.Count) do
      WriteLn(SL[i]);
  finally
    SL.Free;
  end;
  ReadLn;
end.
Zitat:

1.3.csv
1.6.csv
1.7.csv
2.12.csv
2.3.csv
2.6.1.csv
2.6.2.csv
2.6.3.csv
2.8.csv
4.5.csv
4.8.csv
4.9.1.csv
4.9.2.csv
Macht Delphi da was anders?

Uwe Raabe 5. Apr 2023 14:21

AW: Dateinamen auslesen und sortieren
 
Bei Delphi klappt es mit folgender function:
Delphi-Quellcode:
function SortCompare(List: TStringList; Index1, Index2: Integer): Integer;
begin
  Result := string.Compare(List[Index1], List[Index2], [coDigitAsNumbers, coIgnoreCase]);
end;

Nico B. 6. Apr 2023 07:09

AW: Dateinamen auslesen und sortieren
 
Danke für die Antworten.

Ich habe es jetzt mit beiden Versionen probiert.
Allerdings ohne Erfolg...

haentschman 6. Apr 2023 07:59

AW: Dateinamen auslesen und sortieren
 
Quelltext der Versionen, Fehlermeldungen, Dateiliste, Ergebnis? ...wir haben keine Glaskugel. :zwinker:

himitsu 6. Apr 2023 09:18

AW: Dateinamen auslesen und sortieren
 
doch :glaskugel:


sorry, musste sein




Mal provieren die Punkte durch was anderes zu Replacen.
Nicht dass die Dortiermethode denkt intelligent zu sein und den . als Dezimaltrennzeichen ansieht,
denn 1.14 ist als "eine Zahl" wirklich kleiner, als 1.2

PaPaNi 6. Apr 2023 09:25

AW: Dateinamen auslesen und sortieren
 
Zitat:

Zitat von KodeZwerg (Beitrag 1520704)
Habe es nur mal rasch mit FreePascal probiert und stelle nichts falsches fest wenn ich es so mache:

Macht Delphi da was anders?

Wenn ich das richtig verstanden habe, das ist genau das Problem. 2.12 steht bei Dir vor 2.3. Gewollt ist aber anders, weil 12 > 3 ist. Oder irre ich mich?

PaPaNi 6. Apr 2023 10:01

AW: Dateinamen auslesen und sortieren
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1520706)
Bei Delphi klappt es mit folgender function:
Delphi-Quellcode:
function SortCompare(List: TStringList; Index1, Index2: Integer): Integer;
begin
  Result := string.Compare(List[Index1], List[Index2], [coDigitAsNumbers, coIgnoreCase]);
end;

Bei mir funktioniert das auch.

KodeZwerg 6. Apr 2023 13:30

AW: Dateinamen auslesen und sortieren
 
Zitat:

Zitat von PaPaNi (Beitrag 1520730)
Zitat:

Zitat von KodeZwerg (Beitrag 1520704)
Habe es nur mal rasch mit FreePascal probiert und stelle nichts falsches fest wenn ich es so mache:

Macht Delphi da was anders?

Wenn ich das richtig verstanden habe, das ist genau das Problem. 2.12 steht bei Dir vor 2.3. Gewollt ist aber anders, weil 12 > 3 ist. Oder irre ich mich?

2.12 ist kleiner als 2.3 (ausgeschrieben 2.30, vielleicht ist es so besser verständlich), also ja, du irrst Dich oder die Benennung ist schlicht falsch.

Delphi.Narium 6. Apr 2023 13:45

AW: Dateinamen auslesen und sortieren
 
Es handelt sich aber nicht um Dezimalzahlen mit Nachkommastellen, sondern um Nummerierungen, wie sie z. B. auch bei Word, OpenOffice ... üblich sind.

Kapitel 2.3 steht da auch vor Kapitel 2.12, allerdings steht Kapitel 2.30 hinter Kapitel 2.12, ist aber nicht identisch mit Kapitel 2.3.

Im vorliegenden Fall fehlt bei den Zahlen nach dem Punkt genaugenommen die führende 0 und die Sortierung soll die fehlende 0 mit berücksichtigen.

Die Punkte sind letztlich nur optische Trenner zwischen unterschiedlichen Zahlengruppen. Jede Zahlengruppe soll für sich aufsteigend sortiert werden. Und damit ist 3 < 12 und von daher 2.3 vor 2.12 einzusortieren.

Nico B. 6. Apr 2023 14:15

AW: Dateinamen auslesen und sortieren
 
Hallo zusammen,

ja, also Delphi.Narium hat es richtig erfasst und zusammengefasst.
Es handelt sich tatsächlich um eine Struktur wie bei einer Kapitelnummerierung.
Und dort ist die 2.3 vor der 2.12 zu finden.

Anfangs habe ich überhaupt nicht daran gedacht, dass es hier ein Problem geben könnte, da die Dateien im Explorer in der korrekten Reihenfolge angezeigt werden.
Umso überraschter bin ich jetzt, dass es mir so schwer fällt dafür eine einfache Lösung zu finden.

Gibt es denn irgendwo eine Übersicht über die möglichen "Filter" von TCompareOptions?
Dann würde ich da gern weiterlesen um zu verstehen, wo es klemmt und wie es besser geht.

Delphi.Narium 6. Apr 2023 14:31

AW: Dateinamen auslesen und sortieren
 
https://docwiki.embarcadero.com/Libr...CompareOptions

https://docwiki.embarcadero.com/Libr...TCompareOption

Uwe Raabe 6. Apr 2023 14:53

AW: Dateinamen auslesen und sortieren
 
Zitat:

Zitat von Nico B. (Beitrag 1520747)
Gibt es denn irgendwo eine Übersicht über die möglichen "Filter" von TCompareOptions?
Dann würde ich da gern weiterlesen um zu verstehen, wo es klemmt und wie es besser geht.

Wie schon erwähnt ist die Compare-Funktion nicht das Problem - die funktioniert. Es liegt vielleicht daran, wie du sie verwendest. Erstell doch mal ein simples Beispielprojekt. Dann können wir dir vielleicht sagen, was da nicht passt.

PaPaNi 6. Apr 2023 17:50

AW: Dateinamen auslesen und sortieren
 
Kannst du vielleicht damit anfangen? Hier habe ich kurz ein Beispiel ohne string.compare gebastelt. Das funktioniert bei mir im Delphi 2007.

Delphi-Quellcode:
function MySortCompare(List: TStringList; Index1, Index2: Integer): Integer;
var
  wStr1: WideString;
  wStr2: WideString;
  Flags: Dword;
begin
  wStr1 := List[Index1];
  wStr2 := List[Index2];
  Flags := 9; // coDigitAsNumbers + coIgnoreCase
  Result := CompareStringW(LOCALE_USER_DEFAULT, Flags, PWideChar(wStr1), Length(wStr1), PWideChar(wStr2), Length(wStr2)) - CSTR_EQUAL;
end;

procedure Tf_Tester.TestCustomSort;
var
  SL: TStringList;
  i: Integer;
begin
  m_test.Clear;
  SL := TStringList.Create;
  try
    SL.Add('2.6.1.csv');
    SL.Add('4.5.csv');
    SL.Add('4.9.1.csv');
    SL.Add('1.3.csv');
    SL.Add('2.3.csv');
    SL.Add('2.6.2.csv');
    SL.Add('1.7.csv');
    SL.Add('4.9.2.csv');
    SL.Add('1.6.csv');
    SL.Add('2.8.csv');
    SL.Add('2.6.3.csv');
    SL.Add('4.8.csv');
    SL.Add('2.12.csv');

    SL.CustomSort(@MySortCompare);
    for i := 0 to Pred(SL.Count) do begin
      m_test.Lines.Add(SL[i]);
    end;
  finally
    FreeAndNil(SL);
  end;
end;
Zitat:

Das Ergebnis:
1.3.csv
1.6.csv
1.7.csv
2.3.csv
2.6.1.csv
2.6.2.csv
2.6.3.csv
2.8.csv
2.12.csv
4.5.csv
4.8.csv
4.9.1.csv
4.9.2.csv

himitsu 6. Apr 2023 17:57

AW: Dateinamen auslesen und sortieren
 
Delphi-Quellcode:
function MySortCompare(List: TStringList; Index1, Index2: Integer): Integer;
var
  Str1, Str2: string; // oder direkt List[...] benutzen
  Flags: Dword;
begin
  Str1 := List[Index1];
  Str2 := List[Index2];
  Flags := 9; // coDigitAsNumbers + coIgnoreCase
  Result := CompareString(LOCALE_USER_DEFAULT, Flags, PChar(Str1), Length(Str1), PChar(Str2), Length(Str2)) - CSTR_EQUAL;
end;
Warum sinnlos einen langsamen und vor allem unnötigen Speichermanager benutzen, inkl. einiger unnötig aufwändiger/langsamer String-Operationen? (Strings komplett umkopieren)

WideString = nutzt den Speichermanager, bzw. eine API des OLEAuth (OLE32)
UnicodeString = die Wide-Version des AnsiString

AnsiString/UnicodeString/UTF8String/usw. sind LongStrings und verwenden auch eine Referenzzählung, sowie den Delphi-Speichermanager (aktuell FastMM)
PS: Der Typ String[123] ist ein ShortString (nur der Vollständigkeit halber) ... sowie der Typ "String" im Delphi 1 und davor im Turbo Pascal.

PaPaNi 6. Apr 2023 19:38

AW: Dateinamen auslesen und sortieren
 
Zitat:

Zitat von himitsu (Beitrag 1520773)
Delphi-Quellcode:
function MySortCompare(List: TStringList; Index1, Index2: Integer): Integer;
var
  Str1, Str2: string; // oder direkt List[...] benutzen
  Flags: Dword;
begin
  Str1 := List[Index1];
  Str2 := List[Index2];
  Flags := 9; // coDigitAsNumbers + coIgnoreCase
  Result := CompareString(LOCALE_USER_DEFAULT, Flags, PChar(Str1), Length(Str1), PChar(Str2), Length(Str2)) - CSTR_EQUAL;
end;

Ich habe auch damit angefangen (mit CompareString und einfach string-Variablen), aber ich habe eine Fehlermeldung - Index out of range - bekommen. Warum weiß ich momentan nicht. Mit einem anderen Flag (weniger als 8) gab es keine Fehlermeldung, aber die Sortierung war nicht wie gewünscht (was logisch ist). Deshalb habe ich mit CompareStringW gemacht. Hast du selber mit deinem Code ausprobiert? funktioniert wie gewünscht? Welche Delphi-Version?

KodeZwerg 6. Apr 2023 20:05

AW: Dateinamen auslesen und sortieren
 
Wie wäre es hiermit? Getestet mit Delphi Alexandria um eine "logische" Sortierung zu implementieren, dabei ist dann das "Kapitel" da wo es sein sollte.
Delphi-Quellcode:
program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.Classes;

function StrCmpLogicalW(P1, P2: PWideChar): Integer; stdcall; external 'Shlwapi.dll' name 'StrCmpLogicalW';

function CompareProc(List: TStringList; Index1, Index2: Integer): Integer;
begin
  Result := StrCmpLogicalW(PChar(List[Index1]), PChar(List[Index2]));
end;

var
  SL: TStringList;
  i: Integer;
begin
  try
    SL := TStringList.Create;
    try
      SL.Add('2.6.1.csv');
      SL.Add('4.5.csv');
      SL.Add('4.9.1.csv');
      SL.Add('1.3.csv');
      SL.Add('2.3.csv');
      SL.Add('2.6.2.csv');
      SL.Add('1.7.csv');
      SL.Add('4.9.2.csv');
      SL.Add('1.6.csv');
      SL.Add('2.8.csv');
      SL.Add('2.6.3.csv');
      SL.Add('4.8.csv');
      SL.Add('2.12.csv');
      SL.CustomSort(@CompareProc);
      for i := 0 to Pred(SL.Count) do
        WriteLn(SL[i]);
    finally
      SL.Free;
    end;
    ReadLn;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
Zitat:

1.3.csv
1.6.csv
1.7.csv
2.3.csv
2.6.1.csv
2.6.2.csv
2.6.3.csv
2.8.csv
2.12.csv
4.5.csv
4.8.csv
4.9.1.csv
4.9.2.csv

Uwe Raabe 6. Apr 2023 21:45

AW: Dateinamen auslesen und sortieren
 
Am Ende ist das doch alles das gleiche, oder was denkt ihr was die Delphi Methode intern macht?

Das Problem liegt offenbar woanders.

Nico B. 11. Apr 2023 07:12

AW: Dateinamen auslesen und sortieren
 
Hallo zusammen,

dann hier mal mein Code.
Ich muss dazu sagen, dass ich an einem Rechner programmiere, der nicht mit dem Internet verbunden ist.
Ich habe mir zwar beim Abschreiben Mühe gegeben, kann aber nicht ausschließen, dass sich ein Tipp-Fehler eingeschlichen hat.

Delphi-Quellcode:
function Sortierfunktion(Liste: TStringList; Index1: integer; Index2: integer): integer;
begin
  Result:=String.Compare(Liste[Index1],Liste[Index2],[coDigitAsNumbers,coIgnoreCase]);
end;

function AnlagenAuslesen: boolean;
var
  Anlage: TSearchRec;
  DateiListe: TStringList;
  i: integer;
begin
  Result:=true;
  DateiListe:=TStringList.Create;
  DateiListe.Sorted:=true;
  try
    if FindFirst(ExtractFilePath(ParamStr(0)),faAnyFile,Anlage)=0
      then
        try
          begin
            //alle Dateien auslesen
            repeat
              if Anlage.Attr AND faDirectory=faDirectory
                then
                  begin
                    //nichts tun
                  end
                else
                  begin
                    DateiListe.Add(Anlage.Name);
                  end;
            until FindNext(Anlage)<>0;
            //Dateien sortieren
            DateiListe.CustomSort(@Sortierfunktion);
            //Dateien in der richtigen Reihenfolge auslesen
            i:=0;
            while not (DateiListe.Count=i) do
              begin
                DateiAuslesen(ExtractFilePath(ParamStr(0))+DateiListe[i]); //weitere Funktion zum Auslesen der Dateien
                i:=i+1;
              end;
          end;
        finally
          DateiListe.Free;
          FindClose(Anlage);
        end;
  except
    Result:=false;
  end;
end;

KodeZwerg 11. Apr 2023 08:42

AW: Dateinamen auslesen und sortieren
 
DateiListe.Sorted:=true; <<--entfernen!

Nico B. 11. Apr 2023 09:38

AW: Dateinamen auslesen und sortieren
 
Vielen herzlichen Dank auch an Uwe (ohne sein Drängen hätte ich mir wahrscheinlich nicht gleich die Mühe gemacht das Programm abzutippen)!
Jetzt klappt es.

Diese Codezeile wurde in einem anderen Thread zu dem Thema als unbedingt notwendig betitelt.
Und in euren Beispielen habe ich einfach übersehen, dass ihr sie weggelassen habt.

VIELEN, VIELEN DANK noch einmal.
Und entschuldigt, dass ihr euch wegen meiner Dusseligkeit so lange Gedanken dazu machen musstet.

himitsu 11. Apr 2023 10:03

AW: Dateinamen auslesen und sortieren
 
Kommt drauf an

soll automatisch sortiert werden, dann ja, das muß,
soll manuell sortiert werden, weil du hier selber CustomSort aufrufst, dann natürlich nicht.

KodeZwerg 11. Apr 2023 11:41

AW: Dateinamen auslesen und sortieren
 
Hmmm die Embarcadoro Hilfe für TStringList.CustomSort ist aber auch etwas unglücklich beschrieben.
Zitat:

Note: You must explicitly call the CustomSort method. Setting the Sorted property only sorts strings using ANSI (Windows) or UTF-8 (Linux) order, as implemented in the Sort method.
Da sollte eigentlich stehen das beides sich gegenseitig bekämpft und CustomSort immer der Verlierer sein wird :wink:

Benmik 11. Apr 2023 12:16

AW: Dateinamen auslesen und sortieren
 
Wäre
Delphi-Quellcode:
if Anlage.Attr AND faDirectory <> faDirectory
nicht einfacher?

Zudem gebe ich noch zu bedenken, dass du mit dem alleinigen Ausschluss von
Delphi-Quellcode:
faDirectory
sämtliche anderen Attribute (
Delphi-Quellcode:
(faReadOnly, faHidden, faSystem, faArchive,faDevice, faNormal, faTemporary, faSparseFile, faReparsePoint, faCompressed,faOffline, faNotContentIndexed, faEncrypted, faSymLink)
) zulässt. Das mag in deinem Anwendungsfall keine Rolle spielen, sehr generisch ist es nicht.

Nico B. 11. Apr 2023 12:33

AW: Dateinamen auslesen und sortieren
 
Danke für den Hinweis.
Ich habe es jetzt mal beim FindFirst schon die Suche auf '*.csv' eingeschränkt.
Damit hat sich das doch schon erledigt, oder?
Sieht es jetzt besser aus?

Delphi-Quellcode:
    if FindFirst(ExtractFilePath(ParamStr(0))+'*.csv',faAnyFile,Anlage)=0
      then
        try
          begin
            //alle Dateien auslesen
            repeat
              if Anlage.Attr AND faDirectory<>faDirectory
                then
                  DateiListe.Add(Anlage.Name);
            until FindNext(Anlage)<>0;
          usw.usf.

KodeZwerg 11. Apr 2023 12:42

AW: Dateinamen auslesen und sortieren
 
Am einfachsten wäre es vermutlich GetFiles aufzurufen um das daraus entstandene Array zu sortieren und mit was auch immer fortzufahren.

KodeZwerg 11. Apr 2023 12:56

AW: Dateinamen auslesen und sortieren
 
So könnte es beispielsweise aussehen:
Delphi-Quellcode:
program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, System.Classes, System.IOUtils, System.Types;

function StrCmpLogicalW(P1, P2: PWideChar): Integer; stdcall; external 'Shlwapi.dll' name 'StrCmpLogicalW';

function CompareProc(List: TStringList; Index1, Index2: Integer): Integer;
begin
  Result := StrCmpLogicalW(PChar(List[Index1]), PChar(List[Index2]));
end;

var
  SA: TStringDynArray;
  SL: TStringList;
  i: Integer;
begin
  try
    SA := TDirectory.GetFiles(ExtractFilePath(ParamStr(0)));
    SL := TStringList.Create;
    try
      // mit oder ohne vollen Pfad?
      for i := Low(SA) to High(SA) do
        SL.Add(ExtractFileName(SA[i]));
      SL.CustomSort(@CompareProc);
      for i := 0 to Pred(SL.Count) do
        begin
          // mach was mit der Liste
          WriteLn(SL[i]);
        end;
    finally
      SL.Free;
    end;
    ReadLn;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

shebang 11. Apr 2023 14:28

AW: Dateinamen auslesen und sortieren
 
Dann würde ich mir aber die TStringList sparen und direkt auf dem Array arbeiten:
Delphi-Quellcode:
program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses System.SysUtils, System.IOUtils, Generics.Collections, Generics.Defaults;

function StrCmpLogicalW(P1, P2: PWideChar): Integer; stdcall; external 'Shlwapi.dll' name 'StrCmpLogicalW';

var
  i: Integer;
  SA: TArray<string>;
begin
  try
    SA := TDirectory.GetFiles(ExtractFilePath(ParamStr(0)));

    TArray.Sort<string>(SA, TComparer<string>.Construct(
    function(const Left, Right : string) : Integer
    begin
      Result := StrCmpLogicalW(PWideChar(Left), PWideChar(Right));
    end));

    for i := 0 to High(SA) do
    begin
      // mach was mit der Liste
      WriteLn(SA[i]);
    end;
    ReadLn;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.


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