Download Now
Forums

    String Helper Core

    These are the core functions for fast string
    IndexOf
    ,
    Match
    ,
    Replace
    , and
    Split
    for:
    type StringHelper = record helper for string
    The core high level function are:
    function StrFind(const S, SubStr: string; Start: Integer; IgnoreCase: Boolean = False): Integer;
    and
    function StrFind(const S, SubStr: string; Start: Integer; IgnoreCase: Boolean = False): Integer;
    You may want to test
    StrFindBuffer
    and
    StrFindBufferI
    to gauge their performance against similar RTL library routines, as I believe they are significantly faster, and as a consequence all other dependent functions are faster as well.
    type
      TArray<T> = array of T;
      StringArray = TArray<string>;
      IntArray = TArray<Integer>;
    
    function StrFindBuffer(S, SubStr: PChar; SLen, SubStrLen: Integer): Integer;
    var
      Current, Last: Char;
      Lookup: array[Low(Byte)..High(Byte)] of Integer;
      B: Byte;
      I, J, K: Integer;
    begin
      Result := 0;
      if  (SLen = 0) or (SubStrLen = 0) then
        Exit;
      Dec(S);
      Dec(SubStr);
      for I := Low(Lookup) to High(Lookup) do
        Lookup[I] := SubStrLen;
      for I := 1 to SubStrLen - 1 do
      begin
        B := Ord(SubStr[I]);
        Lookup[B] := SubStrLen - I;
      end;
      Last := SubStr[SubStrLen];
      I := SubStrLen;
      while I <= SLen do
      begin
        Current := S[I];
        if Current = Last then
        begin
          J := I - SubStrLen;
          K := 1;
          while K < SubStrLen do
          begin
            if SubStr[K] <> S[J + K] then
              Break;
            Inc(K);
          end;
          if K = SubStrLen then
          begin
            Result := J + 1;
            Exit;
          end;
          B := Ord(Current);
          Inc(I, Lookup[B]);
        end
        else
        begin
          B := Ord(Current);
          Inc(I, Lookup[B]);
        end;
      end;
    end;
    
    function StrFindBufferI(S, SubStr: PChar; SLen, SubStrLen: Integer): Integer;
    var
      Current, Last: Char;
      Lookup: array[Low(Byte)..High(Byte)] of Integer;
      B: Byte;
      I, J, K: Integer;
    begin
      Result := 0;
      if (SubStrLen = 0) or (SLen = 0) then
        Exit;
      Dec(SubStr);
      Dec(S);
      for I := Low(Lookup) to High(Lookup) do
        Lookup[I] := SubStrLen;
      for I := 1 to SubStrLen - 1 do
      begin
        B := Ord(UpCase(SubStr[I]));
        Lookup[B] := SubStrLen - I;
      end;
      Last := UpCase(SubStr[SubStrLen]);
      I := SubStrLen;
      while I <= SLen do
      begin
        Current := UpCase(S[I]);
        if Current = Last then
        begin
          J := I - SubStrLen;
          K := 1;
          while K < SubStrLen do
          begin
            if UpCase(SubStr[K]) <> UpCase(S[J + K]) then
              Break;
            Inc(K);
          end;
          if K = SubStrLen then
          begin
            Result := J + 1;
            Exit;
          end;
          B := Ord(Current);
          Inc(I, Lookup[B]);
        end
        else
        begin
          B := Ord(Current);
          Inc(I, Lookup[B]);
        end;
      end;
    end;
    
    function StrFind(const S, SubStr: string; IgnoreCase: Boolean = False): Integer;
    begin
      if IgnoreCase then
        Result := StrFindBufferI(PChar(S), PChar(SubStr), Length(S), Length(SubStr))
      else
        Result := StrFindBuffer(PChar(S), PChar(SubStr), Length(S), Length(SubStr));
    end;
    
    function StrFind(const S, SubStr: string; Start: Integer; IgnoreCase: Boolean = False): Integer;
    var
      P: PChar;
      I: Integer;
    begin
      P := PChar(S);
      I := Length(S);
      if (Start < 1) or (Start > I) then
      begin
        Result := 0;
        Exit;
      end;
      Dec(Start);
      Inc(P, Start);
      Dec(I, Start);
      if IgnoreCase then
        Result := StrFindBufferI(P, PChar(SubStr), I, Length(SubStr))
      else
        Result := StrFindBuffer(P, PChar(SubStr), I, Length(SubStr));
      if Result > 0 then
        Inc(Result, Start);
    end;
    
    function StrFindCount(const S, SubStr: string; IgnoreCase: Boolean = False): Integer;
    var
      Start, Index: Integer;
    begin
      Result := 0;
      Start := 1;
      repeat
        Index := StrFind(S, SubStr, Start, IgnoreCase);
        if Index > 0 then
        begin
          Inc(Result);
          Start := Index + 1;
        end;
      until Index = 0;
    end;
    
    function StrFindIndex(const S, SubStr: string; IgnoreCase: Boolean = False): IntArray;
    var
      Start, Index: Integer;
    begin
      SetLength(Result, StrFindCount(S, SubStr, IgnoreCase));
      Start := 1;
      Index := 0;
      while Index < Length(Result) do
      begin
        Start := StrFind(S, SubStr, Start, IgnoreCase);
        Result[Index] := Start;
        Inc(Start);
        Inc(Index);
      end;
    end;
    
    function StrReplaceOne(const S, OldPattern, NewPattern: string; IgnoreCase: Boolean = False): string;
    var
      I: Integer;
    begin
      I := StrFind(S, OldPattern, IgnoreCase);
      if I > 0 then
        Result := Copy(S, 1, I - 1) + NewPattern + Copy(S, I + Length(OldPattern), Length(S))
      else
        Result := S;
    end;
    
    function StrReplace(const S, OldPattern, NewPattern: string; IgnoreCase: Boolean = False): string;
    var
      PosIndex: IntArray;
      Diff: Integer;
      I, J, K, L: Integer;
    begin
      PosIndex := StrFindIndex(S, OldPattern, IgnoreCase);
      if Length(PosIndex) = 0 then
      begin
        Result := S;
        Exit;
      end;
      Diff := Length(NewPattern) - Length(OldPattern);
      I := Length(S) + Diff * Length(PosIndex);
      SetLength(Result, I);
      I := 0;
      J := 1;
      K := 1;
      while K <= Length(S) do
      begin
        if K = PosIndex[I] then
        begin
          if I < High(PosIndex) then
            Inc(I);
          Inc(K, Length(OldPattern));
          for L := 1 to Length(NewPattern) do
          begin
            Result[J] := NewPattern[L];
            Inc(J);
          end;
        end
        else
        begin
          Result[J] := S[K];
          Inc(J);
          Inc(K);
        end;
      end;
    end;