Autor |
Beitrag |
Gausi
Beiträge: 8538
Erhaltene Danke: 475
Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
|
Verfasst: Do 12.03.20 16:44
Ich steh hier grade vor einem merkwürdigem Problem.
Gegeben ist StringList, die ich sortieren möchte.
Beispiel:
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:
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:
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?
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
Beiträge: 19285
Erhaltene Danke: 1743
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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:
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
Beiträge: 4785
Erhaltene Danke: 1055
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: 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
Beiträge: 8538
Erhaltene Danke: 475
Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
|
Verfasst: 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 . 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.
_________________ We are, we were and will not be.
|
|
hRb
Beiträge: 269
Erhaltene Danke: 12
|
Verfasst: 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)
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); MyListBox1.Items.Add(s) end; MyListBox.Sorted:=true 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
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) : string;
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 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
Beiträge: 8538
Erhaltene Danke: 475
Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
|
Verfasst: 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 ...
_________________ We are, we were and will not be.
|
|
Sinspin
Beiträge: 1332
Erhaltene Danke: 118
Win 10
RIO, CE, Lazarus
|
Verfasst: 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
Beiträge: 8538
Erhaltene Danke: 475
Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
|
Verfasst: 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.
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.
|
|
|