1

我有一个 TStringList 已排序并包含唯一的文件名。该列表可以是任意大小(因此可以是数十万个条目)。我想检查是否有任何条目以特定字符串开头(即文件是否在子文件夹中)。连续扫描列表并使用 StartsText 很容易,但这不是一个理想的解决方案。

使用 TStringList.Find() 代码作为起点,我创建了一个我认为是解决方案的函数,但我想确定一下。不要担心以下不是类的成员(FList 是正在搜索的 TStringList 实例),StartsFilename 的工作方式与 StartsText 相同:

  function ShortcutFind(const S: string): Boolean;
  var
    L, H, I, C: Integer;
  begin
    Result := False;
    L := 0;
    H := FList.Count - 1;
    while L <= H do begin
      I := (L + H) shr 1;

      if TFilenameUtils.StartsFilename(FList[I], aFolder) then begin
        Result:=TRUE;
        Exit;
      end;

      C := FList.CompareStrings(FList[I], S);
      if C < 0 then
        L := I + 1
      else begin
        H := I - 1;
        if C = 0 then begin
          Result := True;
          if FList.Duplicates <> dupAccept then L := I;
        end;
      end;
    end;
  end;

基本上,唯一真正的变化是它在移动到下一个要比较的条目之前进行检查。

请注意,不能从 TStringList 切换。

这种方法行得通吗?

谢谢

4

1 回答 1

9

如果TFilenameUtils.StartsFilenameStartsText(并且您的第一段暗示它可能)相同,那么您可以通过使用 TStringList.Find而不是复制它来在一个语句中完成整个功能:

var
  I: Integer;
begin
  Assert(not FList.CaseSensitive);
  Result := FList.Find(S, I) or ((I < FList.Count) and StartsText(S, FList[I]));
end;

这应该有效,因为当Find失败时,它仍然会告诉您所需字符串出现在列表中的位置的索引。当您搜索前缀字符串时,它的位置将位于以该前缀开头的任何其他字符串之前,因此如果有任何带有该前缀的字符串,它们将立即出现在前缀本身的假设位置之后。


如果您想保留当前代码,则可以通过删除检查条件来简化它C = 0StartsFilename除非您的功能被破坏,否则这种情况永远不会发生。但是,如果函数真的被破坏并且C 可以为零,那么你至少可以在那个时候停止执行循环,因为你已经找到了你正在寻找的东西。无论哪种方式,您都不需要检查,因为您的函数与返回找到的项目的索引没有Duplicates相同的要求。Find

于 2012-08-16T13:38:07.337 回答