How to concatenate array of string elements into a string
Asked Answered
S

4

10

How should an array of string be converted into string (With separator char)? I mean, is there some system function I can use instead of writing my own function?

Schoening answered 10/5, 2015 at 20:12 Comment(3)
What Delphi version?Systemic
@KenWhite: Version is Delphi2007Schoening
You can use TStringList with Delimiter. Or you can write you own function. I'd shun TStringList. No need to make an instance for this.Larimor
L
10

Since you are using Delphi 2007 you have to do it you self:

function StrArrayJoin(const StringArray : array of string; const Separator : string) : string;
var
  i : Integer;
begin
  Result := '';
  for i := low(StringArray) to high(StringArray) do
    Result := Result + StringArray[i] + Separator;

  Delete(Result, Length(Result), 1);
end;

Simply traverse the array and concat it with your seperator.

And a small test example:

procedure TForm1.FormCreate(Sender: TObject);
begin
  Caption :=StrArrayJoin(['This', 'is', 'a', 'test'], ' ');
end;
Leisaleiser answered 11/5, 2015 at 4:1 Comment(4)
I wrote my own function! Thanks for suggestion!Schoening
Not ideal in terms of speedMetcalf
But readable and easy to understand for anyone who isn't very familiar to Delphi and pointer-stuffScrawl
Please fix the bug in last line of fn body (you delete 1 symbol, but separator is unlimited string). Correct code should be: if (Length(Result)>0) then Delete(Result, 1 + Length(Result) - Length(Separator), Length(Separator)); Why didn't anyone notice this before? 🤦 at least 9 people have this bug.Fealty
S
10

If you're using a more recent version of Delphi you could use TStringHelper.Join, see: http://docwiki.embarcadero.com/Libraries/Sydney/en/System.SysUtils.TStringHelper.Join

Writeln(String.Join(',', ['String1', 'String2', 'String3']));

Output would be: String1,String2,String3

Scrawl answered 28/12, 2020 at 15:30 Comment(0)
M
4

The accepted answer is not ideal in terms of speed, especially if multiple threads are used. This approach is 3x faster on single core, and scales well on SMP.

function JoinStrings(const s: array of string; Delimiter: Char): string;
var
  i, c: Integer;
  p: PChar;
begin
  c := 0;
  for i := 0 to High(s) do
    Inc(c, Length(s[i]));
  SetLength(Result, c + High(s));
  p := PChar(Result);
  for i := 0 to High(s) do begin
    if i > 0 then begin
      p^ := Delimiter;
      Inc(p);
    end;
    Move(PChar(s[i])^, p^, SizeOf(Char)*Length(s[i]));
    Inc(p, Length(s[i]));
  end;
end;

Speed test:

program Project1;

{$APPTYPE CONSOLE}

uses
  Windows, SysUtils, StrUtils, Classes;

function StrArrayJoin(const StringArray: array of string; const Separator: string) : string;
var
  i : Integer;
begin
  Result := '';
  for i := low(StringArray) to high(StringArray) do
    Result := Result + StringArray[i] + Separator;
  Delete(Result, Length(Result), 1);
end;

function JoinStrings(const s: array of string; Delimiter: Char): string;
var
  i, c: Integer;
  p: PChar;
begin
  c := 0;
  for i := 0 to High(s) do
    Inc(c, Length(s[i]));
  SetLength(Result, c + High(s));
  p := PChar(Result);
  for i := 0 to High(s) do begin
    if i > 0 then begin
      p^ := Delimiter;
      Inc(p);
    end;
    Move(PChar(s[i])^, p^, SizeOf(Char)*Length(s[i]));
    Inc(p, Length(s[i]));
  end;
end;

var
  TestData: array of string;

type
  TTestThread = class(TThread)
  protected
    Func: Boolean;
    Count: Integer;
    procedure Execute; override;
  end;

procedure TTestThread.Execute;
var
  dtStart: TDateTime;
  i: Integer;
begin
  dtStart := Now;
  Count := 0;
  repeat
    for i := 1 to 1000 do
      if Func then
        JoinStrings(TestData, ';')
      else
        StrArrayJoin(TestData, ';');
    InterlockedIncrement(Count);
  until Now > dtStart + 1/86400;
end;

procedure TestSmp(CpuCount: Integer; Func: Boolean);
var
  i, c: Integer;
  Threads: array of TTestThread;
begin
  SetLength(Threads, CpuCount);
  for i := 0 to High(Threads) do begin
    Threads[i] := TTestThread.Create(true);
    Threads[i].Func := Func;
    Threads[i].Resume;
  end;
  c := 0;
  for i := 0 to High(Threads) do begin
    Threads[i].WaitFor;
    Inc(c, Threads[i].Count);
    Threads[i].Free;
  end;
  WriteLn(c);
end;

procedure Init();
var
  i: Integer;
begin
  SetLength(TestData, 1000);
  for i := 0 to High(TestData) do
    TestData[i] := DupeString('x', Random(32));
end;

begin
  try
    Init();
    Assert(StrArrayJoin(TestData, ';') = JoinStrings(TestData, ';'));
    TestSmp(1, false);
    TestSmp(1, true);
    TestSmp(4, false);
    TestSmp(4, true);
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
  Readln;
end.

Results: on a quad-core CPU JoinStrings is 12x faster.

StrArrayJoin 1 thread:  55
JoinStrings  1 thread:  184
StrArrayJoin 4 threads: 58
JoinStrings  4 threads: 713
Metcalf answered 2/10, 2017 at 10:6 Comment(0)
F
3

In delphi for .NET you can use the framework Join function , while if you don't want to rely on the .NET framework then you can link the open source JCL library : take a look at the IJclStringList interface of the library .

JclStringList.Join(',');

Otherwise as someone suggested in comments you can simply use a TStringList this way :

arrayList.Delimiter := ',';
arrayList.QuoteChar := '';
joinedArray := arrayList.DelimitedText;

Note that latest version of Delphi XEs(since XE3 if I well remember) have a TStringHelper class that adds the Join method to the String class :

 class function Join(const Separator: string; const Values: 
IEnumerator<string>): string; overload; static;
Figurehead answered 10/5, 2015 at 20:56 Comment(2)
You want the Join overload that accepts an open array of string. Of course, Join, being part of the string helper, is implemented very poorly. The IEnumerable<string> version is broken until XE8, and the efficiency is poor. It's frustrating that Emba's recent RTL quality is so poor.Larimor
Actually setting QuoteChar to '' shouldn't work (at least in Delphi 11 it doesn't seem to be supported when doing TStringList.Create). One has to use a null character (aka #0) instead. See docwiki.embarcadero.com/Libraries/Sydney/en/…Mak

© 2022 - 2024 — McMap. All rights reserved.