String Helper Core
These are the core functions for fast string
IndexOf
, Match
, Replace
, andSplit
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;