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;