Autor Beitrag
Gausi
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8535
Erhaltene Danke: 473

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Do 12.03.20 16:44 
Ich steh hier grade vor einem merkwürdigem Problem.

Gegeben ist StringList, die ich sortieren möchte.
Beispiel:
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
a:--b
a:--c
a:--d
a:-b
a:-c
a:-d
a:---b
a:---c
a:---d


Mit Sort, oder auch mit einer eigenen Sortiermethode auf Basis von AnsiCompareText erhalte ich diese Sortierung:
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
a:-b
a:--b
a:---b
a:-c
a:--c
a:---c
a:-d
a:--d
a:---d

Die "-" werden anscheinend für die Sortierung ignoriert, bzw. haben eine andere Bedeutung.

Ich hätte aber gerne diese Sortierung:
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
a:-b
a:-c
a:-d
a:--b
a:--c
a:--d
a:---b
a:---c
a:---d


Was übersehe ich hierbei, bzw. wie kann ich das ändern? :gruebel:

Verhalten reproduziert in Delphi 2009 und der aktuellen 10.3 CE. Andere "Sonderzeichen" wie ";" oder "_" werden so sortiert wie gewünscht.

_________________
We are, we were and will not be.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Fr 13.03.20 07:51 
AnsiCompareText ist nichts anderes als ein Wrapper um die API-Funktion CompareString. Diese führt eine wortbasierte Sortierung durch. Deshalb werden Bindestriche und Apostrophe bei der Sortierung ignoriert um z.B. Web-Entwickler und Webentwickler zusammen zu lassen.

Man kann das mit einem Flag ändern, aber das nur über die direkte API-Funktion. Ein Beispiel:
ausblenden 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:
function AnsiCompareText2(const S1, S2: string): Integer;
begin
  Result := CompareString(LOCALE_USER_DEFAULT, SORT_STRINGSORT, PChar(S1),
    Length(S1), PChar(S2), Length(S2)) - CSTR_EQUAL;
end;

function StringListCompareExample(List: TStringList; Index1, Index2: Integer): Integer;
begin
  Result := AnsiCompareText2(List[Index1], List[Index2]);
end;

procedure TForm47.FormCreate(Sender: TObject);
var
  MyClass: TStringList;
begin
  MyClass := TStringList.Create;
  try
    MyClass.Text := Memo1.Lines.Text;
    MyClass.CustomSort(StringListCompareExample);
    Memo1.Lines.Text := MyClass.Text;
  finally
    MyClass.Free;
  end;
end;

Das ergibt:
Zitat:
a:---b
a:---c
a:---d
a:--b
a:--c
a:--d
a:-b
a:-c
a:-d
a:blub
a:Chamäleon
a:delta
a:Delta

Bindestriche kommen da natürlich vor Buchstaben und dementsprechend passiert die Sortierung hier genau umgekehrt zu deinem Wunsch. Solange du nur solche Strings hättest, könntest du es natürlich einfach umkehren.

Es gibt übrigens auch noch ein Flag SORT_DIGITSASNUMBERS um "Test 10" nach "Test 2" einzusortieren (ab Windows 7, wie dort auch im Windows Explorer eingeführt).

Für diesen Beitrag haben gedankt: Gausi, icho2099
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Fr 13.03.20 08:38 
Schreib doch einfach eine eigene Compare-Funktion und rufe TStringList.CustomSort auf.

Für diesen Beitrag haben gedankt: Gausi
Gausi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8535
Erhaltene Danke: 473

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Fr 13.03.20 09:46 
Danke, das wird es dann wohl gewesen sein - so was in der Richtung hatte ich auch vermutet. Aber das wird so ein Bugfix, bei dem ich dann drei weitere einbaue :lol:. Ich hatte irgendwie gehofft, dass es da ein globales Setting für gibt.

Die CustomSort-Funktionen sind kein Problem - die sind eh schon im Code drin. Tatsächlich habe ich ObjectLists, die ich nach einer String-Property sortiere. Das kann ich also recht einfach anpassen.

Wo ich dann aufpassen muss ist, dass ich wirklich alle relevanten (ggf. versteckten) Aufrufe von CompareString erwische. Denn diese Listen werden nicht nur sortiert, darin wird dann auch binär gesucht. Und nicht nur einzelne Objekte, sondern auch mal alle, die mit einem bestimmten Teilstring beginnen ... das ist immer ein Spaß, wenn man so tief unten eine Änderung machen muss. :mrgreen:

_________________
We are, we were and will not be.
hRb
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 267
Erhaltene Danke: 12



BeitragVerfasst: Do 19.03.20 01:07 
Hallo Gausi,
ich bin nicht sicher das Probleme korrekt verstanden zu haben. Mir stellt es sich dar, als wäre die Stringlänge (mit Vorrang) wichtiger als das was folgt. Hatte ein vergleichbares Problem.
Ich habe Personenlisten und muss diese "familiengerecht" sortieren, d.h. bei gleichen Nachnamen gilt: Familien zusammenhalten (über Wohnadresse), Eltern vor den Kindern (übers Alter). Ich habe folgende Zeilenstruktur in Richedit:
Name Vorname Alter Straße Stadt weitereDaten ...
Meine Lösung: (verkürzt)
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
MyListBox.Clear;
MyListBox.sorted:=false
for i:=0 to Richedit.Lines.Count-1 do begin
  s:=copy(Name)+copy(Stadt)+copy(Straße)+copy(Alter)+copy(Vorname)+ copy(Originalzeile); //Sortieren nach Name, Adresse, Alter, Vorname 
  MyListBox1.Items.Add(s)
end;
MyListBox.Sorted:=true //Einträge sortieren
Nun vorderen Teil wieder Löschen und alles zurück nach Richedit.

Meine Überlegung also für Dich: Setze Zeilenlänge vor den Sortiertext. Habe folgendes getestet: funktioniert!
1. nehme Richedit, Listbox, Button

ausblenden 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:
function IntToString(w : Longint; i:integer; ch: Char{Ansichar}) : string;
{füllt einen string mit führenden Zeichen bis zur gewünschten stringlänge}
var
  s : string; k:integer;
begin
  Str(w:0,s);
  if Length(s) < i then
     for k:=length(s)+1 to i do  s:=ch + s;
  IntToString:= s;
end;

procedure TForm1.Button1Click(Sender: TObject);
var i: integer; s:string ;
begin
  MyListBox1.Clear;
  MyListBox1.sorted:=false;
  for i:=0 to Richedit1.Lines.Count-1 do
  begin
    s:=Richedit1.Lines[i];
    s:=IntToString(length(s),5,'0')+s;
    MyListBox1.Items.Add(s)
  end;
  MyListBox1.Sorted:=true //Einträge sortieren
end;

In MyListBox steht gewünschtes Sortier-Ergebnis, nun alle Zeilen zurück nach Richedit, dabei die ersten 5 Zeichen der Zeile löschen
Gausi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8535
Erhaltene Danke: 473

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Do 19.03.20 09:01 
Ne, das ist glaube ich was anderes. In deinem Fall würde ich auch nicht über Strings gehen und diese sortieren, sondern über ObjectLists, und in deren CustomSort die enthaltenen Objekte nach den passenden Werten sortieren.

Bei mir sind die Strings, die ich sortieren möchte, Dateipfade, und ich möchte diese nach Ordnern sortiert ausgeben. Wenn ich dabei einen Ordner "Sampler" habe, und einen Ordner "-Sampler", dann kommt es bei meiner alten Methode zu diesem unerwünschtem Verhalten. Dann sind eben nicht alle "Sampler" zusammen und dahinter (oder davor) alle "-Sampler". Ist mir bis heute noch nicht aufgefallen ... :oops:

_________________
We are, we were and will not be.
Sinspin
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1321
Erhaltene Danke: 117

Win 10
RIO, CE, Lazarus
BeitragVerfasst: Do 19.03.20 13:58 
Hallo, das sind intern ja nur Zahlen. Und Minus (ASCII index 45) kommt for allen Zahlen und Buchstaben.

_________________
Wir zerstören die Natur und Wälder der Erde. Wir töten wilde Tiere für Trophäen. Wir produzieren Lebewesen als Massenware um sie nach wenigen Monaten zu töten. Warum sollte unser aller Mutter, die Natur, nicht die gleichen Rechte haben?
Gausi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8535
Erhaltene Danke: 473

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Do 19.03.20 14:55 
Das ist mir schon klar. Aber die Sortiermethoden von Strings sind da schon etwas komplizierter geworden. Wie jaenicke schon richtig schrieb, wird da eine API-Funktion aufgerufen. Und wenn man sich die Parameter so anguckt, wird einem ganz schwummrig. :lol:

Einer dieser Parameter (der bei StringList.Sort bzw. AnsiCompareText verwendet wird) sorgt eben dafür, dass "a-b" und "ab" als "gleich" angesehen werden.

_________________
We are, we were and will not be.