本文整理自网络,侵删。
关键:Find要事先Sort排序,Indexof不用排序。
TStringList内部查找相关的数据。待调试代码时才知道痛苦,浪费无数时间后,只得一步步跟踪,才发
现Find方法返回的Index总是错误的,当时一阵郁闷,随手按下F1键,Find的Help文档展现眼前,对于该
函数是这样描述的:Locates the index for a string in a sorted list and indicates whether a string with that
value already exists in the list.
在Note部分又再次强调:
Only use Find with sorted lists. For unsorted lists, use the IndexOf method instead.
只怪自己一时懒惰,在不了解的情况下便抛弃习惯了的IndexOf,轻易使用新函数。但同时我也来了兴趣,为什么Find只能在使用TStringList.Sort方法后才能正常返回数据呢?
老办法,直接跳到Classes文件中查看源代码:
function TStringList.Find(const S: string; var Index: Integer): Boolean;varL, H, I, C: Integer;beginResult := False;L := 0;H := FCount - 1;while L <= H dobegin I := (L + H) shr 1; C := CompareStrings(FList^[I].FString, S); if C < 0 then L := I + 1 else begin H := I - 1; if C = 0 then begin Result := True; if Duplicates <> dupAccept then L := I; end; end;end;Index := L;end;
还是被吓了一跳,怎么感觉这么复杂,仔细一看才明白,原来是个折半查找算法。呵呵。L,H变量分别代表Low和High,(L + H) shr 1就是求中间值的,完全等于(L + H) div 2,对于二进制,
右移一位就相当于整除2。其中CompareStrings是用来对比两个字符串大小的:
function TStringList.CompareStrings(const S1, S2: string): Integer;beginif CaseSensitive then Result := AnsiCompareStr(S1, S2)else Result := AnsiCompareText(S1, S2);end;
这里的CaseSensitive用来标记是否大小写敏感,AnsiCompareStr是大小写敏感的,AnsiCompareText则
反之。另外在Help文档中还特地说明了两个函数进行判断时,小写字符是小于大写字符的,比如'a'<'A'
。请注意,这一点是与ASCII不相同的地方(如果再跟下去,你可以发现这两个函数是对API的一个封装,
而且封装了Linux和Windows的两个版本)。
此时我们返回到Find函数本身,又会发现在判断条件中只有C<0和C=0的情况,也就是说它只能搜索升序
排列的StringList。
忍不住,再看了看Sort方法。
procedure TStringList.Sort;beginCustomSort(StringListCompareStrings);end;
简单的不能再简单,一行语句。CustomSort是一个公共方法,供用户使用自定义的比较规则进行排序。
StringListCompareStrings参数中放置的就是自定义比较规则的函数:
TStringListSortCompare = function(List: TStringList; Index1, Index2: Integer): Integer;
CustomSort的代码如下:
procedure TStringList.CustomSort(Compare: TStringListSortCompare);beginif not Sorted and (FCount > 1) thenbegin Changing; QuickSort(0, FCount - 1, Compare); Changed;end;end;
Changing和Changed主要是用来触发FOnChanging和FOnChanged的,具体内容可以自己看代码。而
QuickSort则是使用快速排序算法和用户自定义的比较规则进行排序了,再跟入到QuickSort代码中:procedure TStringList.QuickSort(L, R: Integer; SCompare: TStringListSortCompare);varI, J, P: Integer;beginrepeat I := L; J := R; P := (L + R) shr 1; repeat while SCompare(Self, I, P) < 0 do Inc(I); while SCompare(Self, J, P) > 0 do Dec(J); if I <= J then begin ExchangeItems(I, J); if P = I then P := J else if P = J then P := I; Inc(I); Dec(J); end; until I > J; if L < J then QuickSort(L, J, SCompare); L := I;until I >= R;end;
哈哈,正是这一段
while SCompare(Self, I, P) < 0 do Inc(I);while SCompare(Self, J, P) > 0 do Dec(J);
使得TStringList是按照升序排列。至此,大致原因弄明白了。
再看看IndexOf是如何实现搜索的,刚开始我认为它肯定是使用For循环遍历每个Item,遇到相同的内容
则跳出循环,结果发现它确实也是这么做的,只是中间做了一些优化,假如StringList已经排序过,它
会自动使用效率更高的Find方法进行查找,另外它使用Result作为循环变量,对资源的利用极其充分。
代码如下:
function TStringList.IndexOf(const S: string): Integer;beginif not Sorted then Result := inherited IndexOf(S) else if not Find(S, Result) then Result := -1;end;
其中继承使用了父类TStrings中的IndexOf方法
function TStrings.IndexOf(const S: string): Integer;beginfor Result := 0 to GetCount - 1 do if CompareStrings(Get(Result), S) = 0 then Exit;Result := -1;end;
这段代码中的Get方法在TStrings中则是纯虚函数。
function Get(Index: Integer): string; virtual; abstract;
纯虚函数怎么能用,倒。那既然能用,只有一个可能,就是子类TStringList中实现了Get方法。返回到
TStringList中,在果然看到以下代码:
function TStringList.Get(Index: Integer): string;beginif (Index < 0) or (Index >= FCount) then Error(@SListIndexError, Index);Result := FList^[Index].FString;end;
他用来取得指定行的字符串。分析也就此结束。
》》》》》》》》》》》》》》》》》》》》》》》》》》》》
Find是折半查找,速度应当是最快了,而indexof默认是 for 轮回所有item了。 但find应用前必须先排序 sort 不然返回 index错误。
示例如下:
var lst:TStringList ; i:Integer ;begin
lst:=TStringList.Create ;
try
lst:=TStringList.Create ; lst.CaseSensitive :=true; lst.Delimiter :="",""; lst.DelimitedText :=Edit1.Text ;
ShowMessage(IntToStr(lst.IndexOf(Edit2.Text) )); lst.Sort ; if lst.Find(Edit2.Text ,i) then ShowMessage(IntToStr(i)); finally
lst.Free ; end;
eidt2 内容 如下字符串 010a,010A,200a,200b,905a
好的代码像粥一样,都是用时间熬出来的
来源:https://www.cnblogs.com/jijm123/p/11482138.html
相关阅读 >>
Delphi 在xp/2k 下实现 win+ctrl+del 等键的屏蔽的方法
更多相关阅读请进入《Delphi》频道 >>