@Mavarik
so da gibt jetzt der "Hugo" mal noch abschließend seinen Senf hinzu.
Dem Link stimme ich vollends zu.
Bitte schau es dir mal genauer an, was ich mache. Verstanden hast du es auch noch nicht. Der Trick ist nicht das kopieren. Das könnte man in den Quad folgen auch so lösen:
Code:
.....
Dest^ := Src^;
(Dest+1)^ := (Src+1)^;
(Dest+2)^ := (Src+3)^;
(Dest+3)^ := (Src+3)^;
.....
Und es würde nachwievor jede hier dargestellte Version schlagen.
Der Trick ist das Decrementieren des PEnd-Pointers um die Länge des concat-Strings. Somit kann ich in jedem Falle den gesamten concat innerhalb Lenght(Result) div Length(ConcatStr) Loop durchlaufen und ich brauch auch nicht mehr auf die Position zu achten, damit die N-te ein String-X wird.
Somit läuft mein code mit 4x weniger loops als der deine.
Die Loops waren das Performance Problem bei dir und bei Sir Rufo gleich doppelt + der Bremse, daß die
IDE innerhalb jeder Loop die Offsets neu incrementieren muß, da er Str[x] offsets nutzt.
Sir Rufos Bsp könnte man auch so darstellen (nicht hauen wenns jettzt nicht geht -> ungetestet):
Code:
function EH_StrInsertEveryNthPos(const AStr, AInsertStr: string; APos: Integer): string;
var
AStrLen, AInsertStrLen, ResLen, ConcatLen: Integer;
PAStr, Dest, PEnd: PChar;
begin
if AStr = '' then
Result := ''
else begin
AStrLen := PLongInt(NativeUInt(AStr) - SizeOf(LongInt))^; //fast get AStr length
if (AInsertStr = '') or (AStrLen <= APos) then
Result := AStr
else begin
AInsertStrLen := PLongInt(NativeUInt(AInsertStr) - SizeOf(LongInt))^; //fast get AInsertStr length
ResLen := AStrLen+((( AStrLen div APos ) - Ord(AStrLen mod APos = 0))*AInsertStrLen);
ConcatLen := APos+AInsertStrLen;
SetLength(Result, ResLen);
PAStr := Pointer(AStr);
Dest := Pointer(Result);
PEnd := (PAStr+AStrLen)-APos; <<<<<<<<<<<<<<----- Da hat der Frosch die Locken (:
while (PAStr < PEnd) do begin
System.Move(PAStr^, Dest^, APos{$IFDEF
UNICODE} shl 1{$ENDIF});
System.Move(Pointer(AInsertStr)^, (Dest+APos)^, AInsertStrLen{$IFDEF
UNICODE}shl 1{$ENDIF});
Inc(Dest, ConcatLen);
Inc(PAStr,APos);
end;
System.Move(PAStr^, Dest^, ((PEnd+APos) - PAStr){$IFDEF
UNICODE} shl 1{$ENDIF});
end;
end;
end;
und nun noch mal für den Laien verständlicher mit Anleitung und ohne Low-Level code:
Delphi-Quellcode:
function EH_StrInsertEveryNthPos(const AStr, AInsertStr: string; APos: Integer): string;
var
AStrLen, AInsertStrLen, ResLen, ConcatLen: Integer;
PAStr, Dest, PEnd: PChar;
begin
if AStr = '' then
Result := ''
else begin
{ speichere die Länge das Strings, in welchen die Inserts gemacht werden sollen }
AStrLen := Length(AStr);
if (AInsertStr = '') or (AStrLen <= APos) then
Result := AStr
else begin
{ Vorbereitung }
AInsertStrLen := Length(AInsertStr); //speichere die Länge des AInsertStrings
ConcatLen := APos+AInsertStrLen; //speichere die Länge der Fragmente welche im Resultat zusammengesetzt werden
ResLen := AStrLen+((( AStrLen div APos ) - Ord(AStrLen mod APos = 0))*AInsertStrLen); //kalkuliere die Länge des Resultats
SetLength(Result, ResLen); //reserviere den benötigten Speicher für das Resultat
PAStr := Pointer(AStr); //Speichere den Pointer von AStr
Dest := Pointer(Result); //Speichere den Pointer von Result
{ Speichere den Pointer des letzten Chars von AStr abzüglich die Länge von APos }
{ um die Wiederholungen der nachfolgenden Schleife um Faktor APos zu reduzieren }
PEnd := (PAStr+AStrLen)-APos;
{ fülle das Resultat mit den Daten }
while (PAStr < PEnd) do begin
{ da wir PEnd um die Länge von APos reduziert haben, ist es garantiert, das wir NIE die Länge von AStr überschreiten }
{ kopiere eine Menge von APos Chars von der derzeitig gemerkten Stelle des AStr in das Resultat }
System.Move(PAStr^, Dest^, APos*SizeOf(Char));
{ kopiere AInsertString in das Resultat nach den zuletzt kopierten Daten }
System.Move(Pointer(AInsertStr)^, (Dest+APos)^, AInsertStrLen *SizeOf(Char));
{ passe die OffSets an }
Inc(Dest, ConcatLen); //Erhöhe den Pointer des Resultats um die Menge der kopierten Chars
Inc(PAStr, APos); //Erhöhe den Pointer des AStr um APos Stellen
end;
Inc(PEnd+APos); //nun inkrementiere PEnd wieder um APos Stellen, welche wir oben abgezogen haben
{ kopiere den Rest oder garnix (welches sich aus der Differenz von PEnd - PAStr ergibt) von AStr ins Resultat }
System.Move(PAStr^, Dest^, (PEnd - PAStr)* SizeOf(Char));
end;
end;
end;
Ich habs jetzt mal Quick & Dirty mit moves verpackt, da er ja Variable Längen und unterschiedliche Positionen zuläßt.
Die moves arbeiten intern auch mit Loops, somit ist es möglich, das diese Variante nicht unbedingt viel schneller ist.
Jedoch nehm ich der großen Loop(char by char) gleich die Luft raus, indem ich den offset auf die fertige String-Länge zurücksetzte. Und auch nicht mehr schauen muß, ob der Delimiter an der richtigen Position ist oder nicht, es ist garantiert!
Nun ist es aber wieder so, daß das Original leicht verstädlich ist... Und meines wieder nicht. Dennoch behaupte ich, diese Version ist der Version von Sir Rufo weit überlegen .... e.g. Pointer-Offsets!