How to concatenate multiple lines using Move?

How can I concatenate an array of strings using Move. I tried this, but I just can't figure out how to work with Move correctly.

program Project2; {$POINTERMATH ON} procedure Concat(var S: String; const A: Array of String); var I, J: Integer; Len: Integer; begin Len := 0; for I := 0 to High(A) do Len := Len + Length(A[I]); SetLength(S, Length(S) + Len); for I := 0 to High(A) do Move(PWideChar(A[I])[0], S[High(S)], Length(A[I]) * SizeOf(WideChar)); end; var S: String; begin S := 'test'; Concat(S, ['test', 'test2', 'test3']); end. 
+5
source share
2 answers

I would write this function as follows:

 procedure Concat(var Dest: string; const Source: array of string); var i: Integer; OriginalDestLen: Integer; SourceLen: Integer; TotalSourceLen: Integer; DestPtr: PChar; begin TotalSourceLen := 0; OriginalDestLen := Length(Dest); for i := low(Source) to high(Source) do begin inc(TotalSourceLen, Length(Source[i])); end; SetLength(Dest, OriginalDestLen + TotalSourceLen); DestPtr := PChar(Pointer(Dest)) + OriginalDestLen; for i := low(Source) to high(Source) do begin SourceLen := Length(Source[i]); Move(Pointer(Source[i])^, DestPtr^, SourceLen*SizeOf(Char)); inc(DestPtr, SourceLen); end; end; 

This is pretty clear. Complications are caused by blank lines. Any attempt to index empty string characters will throw an exception if range checking is enabled.

To deal with this complexity, you can add if tests for the case when one of the lines involved in the Move call is empty. I prefer a different approach. I prefer the string variable to be a pointer. This bypasses the range check, but also allows you to omit the if .

 Move(Pointer(Source[i])^, DestPtr^, SourceLen*SizeOf(Char)); 

You might think what happens if Source[i] empty. In this case, Pointer(Source[i]) is nil , and you can expect an access violation. In fact, there is no error, because the length of the movement specified by the third argument is zero, and the nil pointer is never canceled.

Another note here:

 DestPtr := PChar(Pointer(Dest)) + OriginalDestLen; 

We use PChar(Pointer(Dest)) , not PChar(Dest) . The latter calls the code to check if Dest empty or not, and if it gives a pointer to one null terminator. We want to avoid executing this code and get the address located in Dest directly, even if it is nil .

+7
source

In the second cycle, you forget that S already the right size to fill all the elements, so you need to use another variable to find out the destination parameter Move

 procedure Concat(var S: String; const A: Array of String); var I, Len, Sum: Integer; begin Len := 0; for I := 0 to High(A) do Inc(Len, Length(A[I])); Sum := Length(S); SetLength(S, Sum + Len); for I := 0 to High(A) do begin if Length(A[I]) > 0 then Move(A[I][1], S[Sum+1], Length(A[I]) * SizeOf(Char)); Inc(Sum, Length(A[I])); end; end; 

Passing the original parameter to PWideChar is completely redundant, as the Move function uses some kind of old general syntax, which allows you to pass everything you need (const Parameter without type).

+3
source

Source: https://habr.com/ru/post/1235072/


All Articles