Hallo,
habe folgendes Problem:
Es sollen Stringlisten sortiert werden, die ähnlich zu
CSV-Dateien über ein Trennzeichen getrennte Spalten haben. Die einzelnen Zeilen verfügen nicht über die gleiche Länge, so dass die Trennzeichen an jeder beliebigen Stelle der Zeile vorkommen können. Die Aufgabe ist nun, die Stringliste ab z. B. dem 4. Trennzeichen aufsteigend zu sortieren.
Dies funktioniert mit einem Nachfahren von TStringList und einer eigenen Vergleichsroutine, die über CustomSort aufgerufen wird. Die dort aufzurufende Funktion benötigt als ersten Parameter eine Stringliste, was mit dem Nachfahren problemlos funktioniert. In der eigenen Vergleichsfunktion kann man aber nicht anstelle des ersten Parameter = TStringList die eigene Klasse übergeben, da hier vom Compiler und dem Vorfahren nur TStringList akzeptiert wird. Die Vergleichsfunktion als Funktion der Klasse zu implementieren funktioniert nicht, da CustomSort eine "klassenlose" Funktion erwartet.
Was mich stört: In der Vergleichsfunktion ist ein Typecast auf den Typ des Nachfahren, also auf die eigene Klasse, zu der die Vergleichsfunktion eigentlich gehört (gehören sollte). Ohne den Typecast bekommt jemand, der die Vergleichsfunktion nur mit einer Stringliste aufruft, ein Problem.
Läßt sich der Typecast irgendwie umgehen? Mir sieht das nicht wirklich objektorientiert aus.
Delphi-Quellcode:
.
.
.
interface
.
.
.
type
tSortStringList = class(TStringList)
private
fSortColumn : Integer;
protected
public
procedure Compare;
published
property SortColumn : Integer Read FSortColumn Write fSortColumn Default 0;
end;
.
.
.
implementation
.
.
.
// Stelle im String ermitteln, ab der sortiert werden soll.
Function GetStartPos(s : String; Delimiter : Char; SortColumn : Integer) : Integer;
Var
iPos : Integer;
iCount : Integer;
begin
iPos := 0;
for iCount := 1 to SortColumn Do iPos := PosEx(Delimiter,s,iPos + 1);
Inc(iPos);
Result := iPos;
end;
// Länge des zu sortierenden Strings ermitteln.
Function GetNextPos(s : String; Delimiter : Char; iStartPos : Integer) : Integer;
Var
iNextPos : Integer;
begin
iNextPos := PosEx(Delimiter,s,iStartPos);
case iNextPos of 0 : iNextPos := Length(s); end;
Result := iNextPos;
end;
function MyCompare(List: TStringList; Index1, Index2: Integer): Integer;
var
iPos1 : Integer;
iPos2 : Integer;
iPosLast1 : Integer;
iPosLast2 : Integer;
iColumn : Integer;
begin
if List is TSortStringList then begin // <-- das stört mich
iColumn := TSortStringList(List).SortColumn;
end else begin
iColumn := 0;
end;
// Startposition der Sortierung im ersten String ermitteln.
iPos1 := GetStartPos(List[Index1],List.Delimiter,iColumn);
// Startposition der Sortierung im zweiten String ermitteln.
iPos2 := GetStartPos(List[Index2],List.Delimiter,iColumn);
// Länge des zu sortierenden (Teil)Strings ermitteln.
iPosLast1 := GetNextPos(List[Index1],List.Delimiter,iPos1);
iPosLast2 := GetNextPos(List[Index2],List.Delimiter,iPos2);
// Groß-/Kleinschreibung beim Vergleich beachten?
if List.CaseSensitive then begin
Result := AnsiCompareStr(Copy(List[Index1],
iPos1,
iPosLast1),
Copy(List[Index2],
iPos2,
iPosLast2)
);
end else begin
Result := AnsiCompareText(Copy(List[Index1],
iPos1,
iPosLast1),
Copy(List[Index2],
iPos2,
iPosLast2)
);
end;
end;
procedure TSortStringList.Compare;
begin
Self.CustomSort(MyCompare);
end;
Ein Aufruf könnte so aussehen: (Sortierung ab dem 4. | aufsteigend und Groß-/Kleinschreibung ignorieren)
Delphi-Quellcode:
Stringliste.Delimiter := '|';
Stringliste.SortColumn := 4;
Stringliste.CaseSensitive := false;
Stringliste.Compare;
Wer hat da eine Idee, wie man's besser machen könnte?