I am testing some enhanced string related functions with which I am trying to use move as a way to copy strings around for faster, more efficient use without delving into pointers.
While testing a function for making a delimited string from a TStringList, I encountered a strange issue. The compiler referenced the bytes contained through the index when it was empty and when a string was added to it through move, index referenced the characters contained.
Here is a small downsized barebone code sample:-
unit UI;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Rtti, System.Classes,
System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.Layouts,
FMX.Memo;
type
TForm1 = class(TForm)
Results: TMemo;
procedure FormCreate(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
function StringListToDelimitedString
( const AStringList: TStringList; const ADelimiter: String ): String;
var
Str : String;
Temp1 : NativeInt;
Temp2 : NativeInt;
DelimiterSize : Byte;
begin
Result := ' ';
Temp1 := 0;
DelimiterSize := Length ( ADelimiter ) * 2;
for Str in AStringList do
Temp1 := Temp1 + Length ( Str );
SetLength ( Result, Temp1 );
Temp1 := 1;
for Str in AStringList do
begin
Temp2 := Length ( Str ) * 2;
// Here Index references bytes in Result
Move ( Str [1], Result [Temp1], Temp2 );
// From here the index seems to address characters instead of bytes in Result
Temp1 := Temp1 + Temp2;
Move ( ADelimiter [1], Result [Temp1], DelimiterSize );
Temp1 := Temp1 + DelimiterSize;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
StrList : TStringList;
Str : String;
begin
// Test 1 : StringListToDelimitedString
StrList := TStringList.Create;
Str := '';
StrList.Add ( 'Hello1' );
StrList.Add ( 'Hello2' );
StrList.Add ( 'Hello3' );
StrList.Add ( 'Hello4' );
Str := StringListToDelimitedString ( StrList, ';' );
Results.Lines.Add ( Str );
StrList.Free;
end;
end.
Please devise a solution and if possible, some explanation. Alternatives are welcome too.
Let's look at the crucial bit of code:
Now, some explanations. When you index a string, you are always indexing characters. You are never indexing bytes. It looks to me as though you wish to index bytes. In which case using the string index operator makes life hard. So I suggest that you index bytes as follows.
First of all initialise Temp1 to 0 rather than 1 since we will be using zero-based indexing.
When you need to index
Result
using a zero-based byte index, do so like this:So your code becomes:
In fact I think I'd write it like this, avoiding all string indexing:
I'd suggest better names than
Temp1
andTemp2
. I also question the use ofNativeInt
here. I'd normally expect to seeInteger
. Not least because a Delphistring
is indexed by signed 32 bit values. You cannot have astring
with length greater than 2GB.Note also that you are not allocating enough memory. You forgot to account for the length of the delimiter. Fix that and your function looks like this:
If you want to avoid pointers, then write it like this: