Delphi で TStringList を異なる方法で並べ替えるにはどうすればよいですか
-
24-09-2019 - |
質問
単純な TStringList があります。それに対して TStringList.Sort を実行します。
次に、アンダースコア「_」が大文字の「A」よりも前にソートされていることに気付きます。これは、同じテキストをソートし、A の後に _ をソートするサードパーティのパッケージとは対照的でした。
ANSI 文字セットによれば、A ~ Z は 65 ~ 90 の文字で、_ は 95 です。したがって、サードパーティのパッケージはその順序を使用しており、TStringList.Sort は使用していないように見えます。
TStringList.Sort の内部を詳しく調べてみると、AnsiCompareStr (大文字と小文字を区別) または AnsiCompareText (大文字と小文字を区別しない) を使用して並べ替えられています。StringList の CaseSensitive 値を true に設定し、次に false に設定して、両方の方法で試しました。ただし、どちらの場合も、「_」が最初にソートされます。
これが TStringList のバグであるとは到底思えません。したがって、ここには私が見ていない何かがあるに違いありません。それは何でしょうか?
私が本当に知る必要があるのは、TStringList を他のパッケージと同じ順序になるように並べ替えるにはどうすればよいかということです。
参考までに、私は Delphi 2009 を使用しており、プログラムで Unicode 文字列を使用しています。
したがって、ここでの最終的な答えは、Ansi 比較を必要なものとオーバーライドすることです (例:非 ANSI 比較) は次のようになります。
type
TMyStringList = class(TStringList)
protected
function CompareStrings(const S1, S2: string): Integer; override;
end;
function TMyStringList.CompareStrings(const S1, S2: string): Integer;
begin
if CaseSensitive then
Result := CompareStr(S1, S2)
else
Result := CompareText(S1, S2);
end;
解決
「正しく」を定義します。
I18N ソートは、完全にロケールに依存します。
だから私は完全に同意します PA これがバグではないこと:デフォルト 選別 動作は、i18nが適切に機能するように設計されているように機能します。
お気に入り ジェリー 言及、 tstringlist.sort 使用します ansicomparestr と ansicompareText (私はそれがどのようにそれを行うかを数行で説明します)。
ただし、TSTRINGLISTは柔軟で、含まれています 選別, 税関 と 比較, 、これはすべて仮想です(したがって、子孫クラスでそれらをオーバーライドできます)
さらに、電話するとき 税関, 、自分でプラグインすることができます 比較 関数。
この答えにはaがあります 比較 あなたが望むことを行う関数:
- 大文字と小文字を区別
- ロケールを使用していません
- 文字列の文字の序数を比較するだけです
税関 これとして定義されています:
procedure TStringList.CustomSort(Compare: TStringListSortCompare);
begin
if not Sorted and (FCount > 1) then
begin
Changing;
QuickSort(0, FCount - 1, Compare);
Changed;
end;
end;
デフォルトでは、 選別 メソッドには非常に単純な実装があり、デフォルトを渡す 比較 呼び出された関数 StringListComparestrings:
procedure TStringList.Sort;
begin
CustomSort(StringListCompareStrings);
end;
だから、あなたがあなた自身を定義するなら tstringListSortCompare 互換性 比較 メソッド、次に、独自のソートを定義できます。
TStringListSortCompareは、TSTRINGLISTを取得するグローバル関数と、比較するアイテムを参照する2つのインデックスとして定義されます。
type
TStringListSortCompare = function(List: TStringList; Index1, Index2: Integer): Integer;
使用できます StringListComparestrings あなた自身を実装するためのガイドラインとして:
function StringListCompareStrings(List: TStringList; Index1, Index2: Integer): Integer;
begin
Result := List.CompareStrings(List.FList^[Index1].FString,
List.FList^[Index2].FString);
end;
したがって、デフォルトでは、tstringlist.sortをtlist.comparesstringsにduredersに留めます。
function TStringList.CompareStrings(const S1, S2: string): Integer;
begin
if CaseSensitive then
Result := AnsiCompareStr(S1, S2)
else
Result := AnsiCompareText(S1, S2);
end;
次に、下にあるWindows API関数を使用します 比較 デフォルトのユーザーロケール付き 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;
最後に 比較 必要な機能。繰り返しますが、制限:
- 大文字と小文字を区別
- ロケールを使用していません
- 文字列の文字の序数を比較するだけです
これはコードです:
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は閉鎖されていませんが、まったく逆です。多くの場合、本当に柔軟なアーキテクチャです。
多くの場合、その柔軟性をどこで接続できるかを見るのは少し掘り下げています。
- ジェロエン
他のヒント
ansicomparest / ansicompareTextは、文字番号以上のものを考慮に入れます。ユーザーのロケールを考慮しているため、「e」は「é」、「ê」などと一緒に並べ替えます。
ASCII注文で並べ替えるには、カスタム比較関数を使用します ここで説明するように
AnsiCompareStr (CompareString with LOCALE_USER_DEFAULT) には、句読点を持つ文字が等しいものとして取得されるため、エラーが発生します。
E1é1E2é2
正しい順序は次のとおりです (たとえば、チェコ語の場合)。
E1E2é1é2
注文時にこのエラーを回避する方法を知っている人はいますか?
2010 年 11 月 2 日:記述された行動は完全に言語規則に従っていることをお詫びしなければなりません。愚かで「悪い」と思いますが、API 関数のエラーではありません。
Windows XP のエクスプローラーは、いわゆる直感的なファイル名の順序を使用して、より良い結果を提供しますが、プログラム的に使用することはできません。