9

是否有任何 Delphi D2010 函数(如 PosEx)从字符串末尾开始在字符串中查找子字符串?

我正在删除对 FastStrings 库的所有调用,我使用的函数之一是 FastPosBack:

function FastPosBack(const aSourceString, aFindString : AnsiString; const aSourceLen, aFindLen, StartPos : Integer) : Integer;

我找到了 LastDelimiter 但它并不完全相同,因为它只找到最后一个分隔符并且我无法指定起始位置。

谢谢!

更新:在 DR 评论之后,我创建了这个函数:

function FastPosBack(const aSourceString, aFindString : String; const aSourceLen, aFindLen, StartPos : Integer) : Integer;
var
  RevSourceString, RevFindString: string;
begin
  RevSourceString := AnsiReverseString(aSourceString);
  RevFindString := AnsiReverseString(aFindString);

  Result := Length(aSourceString) - PosEx(RevFindString, RevSourceString, StartPos) + 1;
end;

有没有更有效的方法来做到这一点?在 1000000 个循环周期中,Pos 需要 47 毫秒,而 FastPosBack 需要 234 毫秒才能完成。

4

7 回答 7

11

试试这个/这些:

function RPos(const aSubStr, aString : String; const aStartPos: Integer): Integer; overload;
var
  i: Integer;
  pStr: PChar;
  pSub: PChar;
begin
  pSub := Pointer(aSubStr);

  for i := aStartPos downto 1 do
  begin
    pStr := @(aString[i]);
    if (pStr^ = pSub^) then
    begin
      if CompareMem(pSub, pStr, Length(aSubStr)) then
      begin
        result := i;
        EXIT;
      end;
    end;
  end;

  result := 0;
end;


function RPos(const aSubStr, aString : String): Integer; overload;
begin
  result := RPos(aSubStr, aString, Length(aString) - Length(aSubStr) + 1);
end;

重载提供了一种调用 RPos 的方法,该方法使用最有效的 startpos 从字符串的末尾进行搜索,而无需自己计算。为了提高效率,明确指定时不会对 startpos 执行检查。

在我的 SmokeTest 性能测试套件中,这比您的 FastPosBack 快约 20%(顺便说一句,它包含一个“off by one”错误,并且需要一些它实际上不使用的参数)。

于 2009-10-10T22:44:01.733 回答
8

您可以PosReverseString(来自 StrUtils)结合使用

于 2009-10-10T14:32:32.297 回答
3

SearchBufDelphi在 StrUtils 单元中带有一个可以向后搜索的功能。但是,它专门用于搜索单词,因此它的行为可能与您想要的不太一样。下面我把它包装成一个匹配你想要的接口的函数。

function FastPosBack(const aSourceString, aFindString: AnsiString;
                     const aSourceLen, aFindLen, StartPos: Integer): Integer;
var
  Source, Match: PAnsiChar;
begin
  Source := PAnsiChar(ASourceString);
  Match := SearchBuf(Source, ASourceLen, ASourceLen, 0,
                     AFindString, [soMatchCase]);
  if Assigned(Match) then
    Result := Match - Source + 1
  else
    Result := 0;
end;
于 2009-10-10T15:57:25.760 回答
2

首先,考虑是否需要速度优化的解决方案。如果它不太可能在实际使用中被调用 100000 次,那么反转字符串并使用现有的子字符串搜索就可以了。

如果速度是一个问题,那么有很多很好的资源可以让你自己编写。在维基百科上寻找“字符串搜索算法”的想法。当我在电脑前时,我会发布一个链接和一个示例算法。我现在正在用手机输入这个。

更新:

这是我承诺的例子:

function RPOS(pattern: string; text:string): Integer;
var patternPosition,
    textPosition: Integer;
begin
  Result := -1;
  for textPosition := Length(text) downto 0 do
  begin
    for patternPosition := Length(pattern) downto 0 do
      if not (pattern[patternPosition] = (text[textPosition - (Length(pattern) - patternPosition)])) then
        break;
    if patternPosition = 0 then
      Result := textPosition -Length(pattern) + 1;
  end;
end;

它基本上是一种倒置的朴素(蛮力)字符串搜索算法。它从模式和文本的末尾开始,一直到开头。我可以保证它比 Delphi 的 Pos() 函数效率低,尽管我不能说它是否比 Pos()-ReverseString() 组合更快或更慢,因为我没有测试过它。其中有一个错误,我还没有找到原因。如果两个字符串相同,则返回 -1(未找到)。

于 2009-10-10T16:04:59.537 回答
2

我使用来自 Free Pascal 的 strutils 函数的 RPOS 变体:

http://svn.freepascal.org/cgi-bin/viewvc.cgi/trunk/rtl/objpas/strutils.pp?view=markup

string,string 版本与 Deltics 几乎相同,但有变体:

Function RPosEX(C:char;const S : AnsiString;offs:cardinal):Integer; overload;
Function RPosex (Const Substr : AnsiString; Const Source : AnsiString;offs:cardinal) : Integer; overload;
Function RPos(c:char;const S : AnsiString):Integer; overload;
Function RPos (Const Substr : AnsiString; Const Source : AnsiString) : Integer; overload;

它们是根据 FPC 的 LGPL+linking exception 许可证获得许可的,但由于我编写了它们,我特此在 BSD 许可证下发布它们。

于 2009-10-11T17:28:44.103 回答
1

不在标准 RTL 中,而是在 INDY 中(根据在线帮助的单位 idGlobalProtocols),这是最近 Delphi 安装的一部分:

function RPos(
    const ASub: String, 
    const AIn: String, 
    AStart: Integer = -1
): Integer;
于 2009-10-10T15:47:26.093 回答
1

在进行搜索之前添加大写或小写 aSubstr 和 aString 参数可能会使 Deltics 目的不区分大小写。我认为他在致电 RPos 之前让您这样做。但也许一个可选参数可以完成这项工作。

这就是 Deltic 的目的应该是这样的:

function RPos(const aSubStr, aString : String; const aStartPos: Integer;
              const aCaseInSensitive:boolean=true): Integer; overload;
var
  i, _startPos: Integer;
  pStr: PChar;
  pSub: PChar;
  _subStr, _string: string;
begin

 if aCaseInSensitive then
 begin
  _subStr := lowercase( aSubstr );
  _string := lowercase( aString );
 end
 else 
 begin
  _subStr := aSubstr:
  _string := aString;
 end;

 pSub := Pointer(_subStr);

 if aStartPos = -1 then
    _startPos :=  Length(_string) - Length(_subStr) + 1
 else
    _startPos := aStartPos;

 for i := _startPos downto 1 do
 begin
   pStr := @(_string[i]);
   if (pStr^ = pSub^) then
   begin
     if CompareMem(pSub, pStr, Length(_subStr)) then
     begin
       result := i;
       EXIT;
     end;
   end;
 end;

 result := 0;
end;


function RPos(const aSubStr, aString : String; 
              const aCaseInSensitive:boolean=true): Integer; overload;
begin
  result := RPos(aSubStr, aString, Length(aString) - Length(aSubStr) + 1,
                 aCaseInSensitive);
end;
于 2011-08-04T01:49:49.033 回答