有像 PosEx 这样的内置 Delphi 函数可以找到从字符串后面开始的子字符串吗?

问题描述 投票:0回答:7

是否有像 PosEx 这样的 Delphi D2010 函数可以从字符串末尾开始查找字符串内的子字符串?

我正在删除对 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 需要 47ms,而 FastPosBack 需要 234ms 才能完成。

delphi delphi-2009 delphi-2010
7个回答
14
投票

试试这个/这些:

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%(它顺便包含一个“相差一”错误,并且需要一些实际上并不使用的参数)。


9
投票

您可以将

Pos
ReverseString
(来自 StrUtils)结合使用


3
投票

Delphi自带了一个可以向后搜索的功能,在StrUtils单元中

SearchBuf
。不过,它专门用于搜索单词,因此它的行为可能不太符合您想要的方式。下面我将它包装成一个与您想要的接口相匹配的函数。

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;

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(未找到)。


2
投票

我使用 FreePascal 的

RPOS
函数的
strutils
变体:

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;<br>
Function RPosex (Const Substr : AnsiString; Const Source : AnsiString;offs:cardinal) : Integer; overload;<br>
Function RPos(c:char;const S : AnsiString):Integer; overload;<br>
Function RPos (Const Substr : AnsiString; Const Source : AnsiString) : Integer; overload;

它们是根据 FPC 的 LGPL+链接例外许可证获得许可的,但自从我编写它们以来,我特此在 BSD 许可证下发布它们。


1
投票

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

function RPos(
    const ASub: String, 
    const AIn: String, 
    AStart: Integer = -1
): Integer;

1
投票

也许在搜索之前添加大写或小写的 aSubstr 和 aString 参数可以使 Deltics 的目的大小写不敏感。我认为他让你在打电话给 RPo 之前先做这件事。但也许一个可选参数可以完成这项工作。

这就是 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;
© www.soinside.com 2019 - 2024. All rights reserved.