• Status: Solved
• Priority: Medium
• Security: Public
• Views: 707

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

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
wantime
• 8
• 7
• 4
• +2
4 Solutions

Commented:
0

Commented:
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

Commented:
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 Commented:
thanks.

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

0

Commented:
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 Commented:
okay, just use '[ ' and '] '
i will have a try...
0

Commented:
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

Commented:
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

Commented:
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 Commented:
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

Freelance Project ManagerCommented:
> 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

Commented:
>>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

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

Freelance Project ManagerCommented:
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

Freelance Project ManagerCommented:
oups , bold does not work in code section ( and ) ...
0

Commented:

@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

Freelance Project ManagerCommented:
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

Commented:
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

Freelance Project ManagerCommented:
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

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

Author Commented:
thanks for the different solution and the analyse.
0

Commented:
I knew somebody would show a TStringlist solution - I just had no idea what it would be :D
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.