Because I need this eventually, here the full quote of my answer in [Wayback] sorting – How can I get TStringList to sort differently in Delphi – Stack Overflow (The default Sort behaviour is to accommodate i18n sorting in natural order):
i18n sorting totally depends on your locale.
So I totally agree with PA that this is not a bug: the default Sort behaviour works as designed to allow i18n to work properly.Like Gerry mentions, TStringList.Sort uses AnsiCompareStr and AnsiCompareText (I’ll explain in a few lines how it does that).
But: TStringList is flexible, it contains Sort, CustomSort and CompareStrings, which all are virtual (so you can override them in a descendant class)
Furthermore, when you call CustomSort, you can plug in your own Compare function.At the of this answer is a Compare function that does what you want:
- Case Sensitive
- Not using any locale
- Just compare the ordinal value of the characters of the strings
CustomSort is defined as this:
procedure TStringList.CustomSort(Compare: TStringListSortCompare); begin if not Sorted and (FCount > 1) then begin Changing; QuickSort(0, FCount - 1, Compare); Changed; end; end;
By default, the Sort method has a very simple implementation, passing a default Compare function called StringListCompareStrings:
procedure TStringList.Sort; begin CustomSort(StringListCompareStrings); end;
So, if you define your own TStringListSortCompare compatible Compare method, then you can define your own sorting.
TStringListSortCompare is defined as a global function taking the TStringList and two indexes referring the items you want to compare:
type TStringListSortCompare = function(List: TStringList; Index1, Index2: Integer): Integer;
You can use the StringListCompareStrings as a guideline for implementing your own:
function StringListCompareStrings(List: TStringList; Index1, Index2: Integer): Integer; begin Result := List.CompareStrings(List.FList^[Index1].FString, List.FList^[Index2].FString); end;
So, by default TStringList.Sort defers to TList.CompareStrings:
function TStringList.CompareStrings(const S1, S2: string): Integer; begin if CaseSensitive then Result := AnsiCompareStr(S1, S2) else Result := AnsiCompareText(S1, S2); end;
Which then use the under lying Windows API function CompareString with the default user locale LOCALE_USER_DEFAULT:
function AnsiCompareStr(const S1, S2: string): Integer; begin Result := CompareString(LOCALE_USER_DEFAULT, 0, PChar(S1), Length(S1), PChar(S2), Length(S2)) - 2; end; function AnsiCompareText(const S1, S2: string): Integer; begin Result := CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, PChar(S1), Length(S1), PChar(S2), Length(S2)) - 2; end;
Finally the Compare function you need. Again the limitations:
- Case Sensitive
- Not using any locale
- Just compare the ordinal value of the characters of the strings
This is the code:
function StringListCompareStringsByOrdinalCharacterValue(List: TStringList; Index1, Index2: Integer): Integer; var First: string; Second: string; begin First := List[Index1]; Second := List[Index2]; if List.CaseSensitive then Result := CompareStr(First, Second) else Result := CompareText(First, Second); end;
Delphi ain’t closed, quite the opposite: often it is a really flexible architecture.
It is often just a bit of digging to see where you can hook into the that flexibility.
–jeroen