Solved

# Delphi2010: how to split string in an array and then save them to a new array?

Posted on 2010-11-18
625 Views
hi,

say there is array A contains following value:

A[0] = "[ 0 ] yy(z)"
A[1 ] = "[ 1 ] yyy(zz)"
A[ 2 ]="[ 12 ] yy(zz)"
A[ 2 ]="[ 123 ] yy(zzz)"

the format of the string are:  [ number ]String(String)
the length is flexible.

how could i get all number of this array and save these numbers into a new array?

thanks,

wantime

0
Question by:wantime
• 8
• 7
• 4
• +2

LVL 13

Expert Comment

0

LVL 13

Expert Comment

Auurgh stupid code attachment facility!!!!!!!!!!!!!!!!!!
``````Well you'll  need a function that will take the number out of your '[0] yy(z)' string:
//Note: This code is UNTESTED
function CleanNumber(aString) : integer;
var
PosOpenBracket : integer;
PosCloseBracket : integer;
begin
result := -1;
result :=  MidStr(aString, PosOpenBracket, PosCloseBracket - PosOpenBracket);
//Note: if the above line isn't working, try this:
//result :=  MidStr(aString, PosOpenBracket - 1, PosCloseBracket - PosOpenBracket - 1);
end;

Then you could iterate through your array, and insert into a new array after cleaning
var
i  : integer;
newarray : array of integer;
begin
setlength(newarray, 0);
for i := 0 to length(A) do
begin
setlength(newarray, length(newarray + 1));
newarray[i] := CleanNumber(A[i]);
end;
end;

Note: it may be more effecient to not set the length of the array every time you add a new element. IF you know the length of the array must be the same as the length of A, then just set it once:
var
i  : integer;
newarray : array of integer;
begin
setlength(newarray, length(A));
for i := 0 to length(A) do
newarray[i] := CleanNumber(A[i]);
end;
``````
0

LVL 13

Expert Comment

Oh shucks, you may get an access violation because it should be:
for i := 0 to length(A) -1 do
(note the -1)
0

Author Comment

thanks.

but one more problem is that in each [ 0 ] there are two spaces before and after the nummber.

0

LVL 13

Accepted Solution

rfwoolf earned 125 total points
ahh okay then we can use trim function to remove any spaces / line breaks / carriage returns between the square brackets []'s
We also put a try..except statement to try catch in problems if it tries to convert a bad string into an integer.

Also I see a mistake with my code in that the result must be INTEGER, but midstr returns a string.
Thus it should be something like this:

//Still untested
function CleanNumber(aString) : integer;
var
PosOpenBracket : integer;
PosCloseBracket : integer;
begin
result := -1;
try
result :=  StrToInt(Trim(MidStr(aString, PosOpenBracket, PosCloseBracket - PosOpenBracket)));
except
//Catch the exception. And do nothing unless you disagree. The result will be -1
end;
//Note: if the above line isn't working, try this:
//result :=  MidStr(aString, PosOpenBracket - 1, PosCloseBracket - PosOpenBracket - 1);
end;
0

Author Comment

okay, just use '[ ' and '] '
i will have a try...
0

LVL 13

Expert Comment

You must also watch out for AnsiPos. It returns the position of '[' and ']' but it might be 1-based, in other words if the first character is '[' it will return 1,
but,
MidStr might be 0-based, in other words '1' means 2nd position because '0' means first position.

So you may have to change the line with "MidStr" on it to fix this problem.
That's why you must test it.
0

LVL 32

Expert Comment

Just change CleanNumber to

function CleanNumber(const aString: string) : integer;
var
loop: Integer;
temp: string;
begin
result := -1;
temp := '';
for loop := 1 to Length(aString) do
if IsNumeric(aString[loop]) then
temp := temp + aString[loop];
Result := StrToIntDef(temp, 0);
end;
0

LVL 13

Expert Comment

That's a very nice optimization.
StrToIntDef also catches any problems converting StrToInt, and you can put 0 or -1 depending on your preference.
0

Author Comment

it works with the solution by using Pos: i have changed AnsiPos to Pos.

btw, is method isNumeric a method from delphi class or should one write it himself like this:

function isNumeric(const S: string): Boolean;
var
P: PChar;
begin
P      := PChar(S);
Result := False;
while P^ <> #0 do
begin
if not (P^ in ['0'..'9']) then Exit;
Inc(P);
end;
Result := True;
end;
0

LVL 25

Expert Comment

> That's a very nice optimization.
using IsNumeric to test each character ?
and to build the string char after char ?
That is no optimization at all. Your code was the right way to do it, rfwoolf. Use of StrToIntDef is certainly a convenience, it takes less code to do the same (but with some additional function calls). Just take your code, add this StrToIntDef and fix the eventual +/-1 there might be in your position using MidStr.

You can also use PosEx to look for the closing bracket only AFTER the opening bracket

Using Copy function, the correct code (having the 2 brackets positions) is this :

``````function CleanNumber(aString) : integer;

var

PosOpenBracket : integer;

PosCloseBracket : integer;

begin

Result:=-2; // default value for brackets errors

PosOpenBracket := Pos('[', aString);

if PosOpenBracket<1 Then Exit;

PosCloseBracket := PosEx(']', aString, PosOpenBracket);

if PosCloseBracket <1 Then Exit;

Result:=

StrToIntDef(Trim(

Copy(aString,PosOpenBracket+1,PosCloseBracket-PosOpenBracket-1)

),-1); // -1 is the value for invalid int value inside the brackets, spaces non-withstanding

end;
``````
0

LVL 13

Expert Comment

>>using IsNumeric to test each character ?
>>and to build the string char after char ?
Yes, in my mind this parses the string once only, instead of... 3 times

But if someone says my solution is correct, who am I to argue :D
0

LVL 25

Expert Comment

well if you want to be certain , execute 1000.000 times all algo , mesure time and see which one is best
0

LVL 25

Assisted Solution

epasquier earned 125 total points
Pos only do one atomic comparison for each char, that is much quicker than doing IsNumeric for each char which is a set test operation.
PosEx ensures that the next search is done only from where the last stop. To gain one operation, it should even be
``````PosCloseBracket := PosEx(']', aString, PosOpenBracket [b]+1[/b] );
``````
Then Copy just do one Move operation with the correct number of chars.
Only Trim is probably a bit expensive, but it is so handy...

With ewangoya algo, all characters are scanned, always. on a string like
[ 10 ] alongstring(anevenlongerstring)
the difference in performance would start to be very noticeable
A string like
[ 10 ] 2be3(whatever7)
would return 10237 as value, which is not correct. only by checking bracket positions such inconvenience can be avoided.
0

LVL 25

Expert Comment

oups , bold does not work in code section ( and ) ...
0

LVL 32

Assisted Solution

ewangoya earned 125 total points

@epasquier
[ 10 ] 2be3(whatever7)
I know the limitation, but look at the authors specification
The same argument you would use with [ 10, ] 2be3(whatever7)

Using Pos is obviously faster (I've not claimed otherwise) but we are not talking of a million items
1000 lines is nothing, 100,000 lines is nothing, both algorithms should take less that 1/4 of a second for 100,000 items

Here's how you put it all together

``````function IsNumeric(const AValue: string): Boolean;

var

I: Integer;

begin

for I := 1 to Length(AValue) do

case AValue[I] of

'0'..'9':

;

else

begin

Result := False;

Exit;

end;

end;

Result := Length(AValue) > 0;

end;

function CleanNumber(const AValue: string): Integer; //using IsNumeric

var

I: Integer;

temp: string;

begin

temp := '';

for I := 0 to Length(AValue) -1 do

if IsNumeric(AValue[I]) then

temp := temp + AValue[I];

Result := StrToIntDef(Temp, -1);

end;

function CleanNumber(const AValue: string) : integer;  //using Pos

var

PosOpenBracket: Integer;

PosCloseBracket: Integer;

begin

Result := -1;

PosOpenBracket := Pos('[', AValue);

if PosOpenBracket < 1 then

Exit;

PosCloseBracket := Pos(']', AValue);

if PosCloseBracket < 1 then

Exit;

Result:= StrToIntDef(Trim(Copy(AValue, PosOpenBracket +1 ,PosCloseBracket-PosOpenBracket -1)), -1);

end;

procedure CopyArrayElements;

var

I: Integer;

Destination: array of integer;

begin

SetLength(Destination, Length(SourceArray));

for I := 0 to Length(SourceArray) -1 do

Destination[I] := CleanNumber(SourceArray[I]);

end;
``````
0

LVL 25

Expert Comment

FYI : compact IsNumeric function
``````function IsNumeric(const AValue: string): Boolean;

var i: Integer;

begin

i:=Length(AValue);

Result:=i>0;

While (i>0) And Result do

begin

Result:=(AValue[i]>='0') And (AValue[i]<='9');

Dec(i);

end;

end;
``````
0

LVL 24

Assisted Solution

jimyX earned 125 total points
Just to show a different way of doing that by using ExtractStrings and Tstringlist which will extract all the numbers in any string you pass and add them to the Tstringlist items by defining the unwanted characters.

The code below will fill a Tstringlist items with the numbers among the string you pass as follows:

'[ 123 ] yy(zzz)'
will result in:
123            // in the Stringlist[0]

'[ 123 ] yy 3 (zzz)'
will result in:
123           // in the Stringlist[0]
3               // in the Stringlist[1]

'4  [ 123 ] yy 3 (zzz)'
will result in:
4               // in the Stringlist[0]
123           // in the Stringlist[1]
3               // in the Stringlist[2]

and so on,
``````var
Str:TStringlist;
begin
Str := TStringlist.Create;
// any chars other than 0..9 will be considered as separators
ExtractStrings(['[',']','(',')','a'..'z','A'..'Z'],[' '],'[ 123 ] yy(zzz)',Str);
// for showing the first item which will be 123
showmessage(IntToStr(StrToInt(Trim(Str[0]))));

Str.Free;
``````
0

LVL 25

Expert Comment

That's a costly but compact solution. Thanks for sharing it.
But you should declare the set this way, which is more correct and easier to understand at first look :

``````ExtractStrings( [ 0..#255] - ['0'..'9'],[],'[ 123 ] yy(zzz)',Str);
``````
0

LVL 25

Expert Comment

where is gone my # before 0 ? am I sleeping already or that new editor just swallowed it ?
0

Author Comment

thanks for the different solution and the analyse.
0

LVL 13

Expert Comment

I knew somebody would show a TStringlist solution - I just had no idea what it would be :D
0

## Featured Post

A lot of questions regard threads in Delphi. Â  One of the more specific questions is how to show progress of the thread. Â  Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to theâ€¦
Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have toâ€¦
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Itemsâ€¦
You have products, that come in variants and want to set different prices for them? Watch this micro tutorial that describes how to configure prices for Magento super attributes. Assigning simple products to configurable: We assigned simple productsâ€¦