Любая встроенная функция Delphi, такая как PosEx, которая находит подстроку, начиная с конца строки? - PullRequest
9 голосов
/ 10 октября 2009

Существует ли какая-либо функция 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 мс.

Ответы [ 7 ]

8 голосов
/ 11 октября 2009

Попробуйте это / эти:

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, если это явно указано.

В моем пакете тестирования производительности SmokeTest это происходит примерно на 20% быстрее, чем ваш FastPosBack (который случайно содержит ошибку «отключено одним», а также требует некоторых параметров, которые он фактически не использует).

8 голосов
/ 10 октября 2009

Вы можете использовать Pos в сочетании с ReverseString (из StrUtils)

3 голосов
/ 10 октября 2009

Delphi поставляется с функцией поиска в обратном направлении, SearchBuf в блоке 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;
2 голосов
/ 11 октября 2009

Я использую варианты RPOS из функции Strutils Free Pascal:

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

строка, строковая версия почти такая же, как у Deltics ', но есть варианты:

</p> <p>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;

Они лицензированы в зависимости от лицензии исключения FPPL LGPL +, но с тех пор, как я их написал, я выпускаю их под лицензией BSD.

2 голосов
/ 10 октября 2009

Во-первых, подумайте, нужно ли оптимизированное по скорости решение. Если маловероятно, что при реальном использовании он будет вызываться 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;

По сути, это алгоритм поиска строк с перевернутой наивностью. Он начинается в конце как шаблона, так и текста и работает до самого начала. Я могу гарантировать, что она менее эффективна, чем функция Pos () Delphi, хотя я не могу сказать, является ли она более быстрой или медленной, чем комбинация Pos () - ReverseString (), поскольку я ее не тестировал. В этом есть ошибка, причина которой я не нашел. Если две строки идентичны, возвращается -1 (не найдено).

1 голос
/ 04 августа 2011

Возможно, добавление в верхний или нижний регистр параметров aSubstr и aString перед выполнением поиска может сделать Deltics нечувствительным к регистру. Я думаю, что он оставил вас сделать это, прежде чем звонить в RPos. но, возможно, дополнительный параметр может сделать работу.

Вот как должна выглядеть цель Делтика:

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;
1 голос
/ 10 октября 2009

Не в стандартном RTL, а в INDY (единица idGlobalProtocols согласно онлайн-справке), которая является частью недавних установок Delphi:

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