Solved

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

Posted on 2010-11-18
22
625 Views
Last Modified: 2012-06-22
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
Comment
Question by:wantime
  • 8
  • 7
  • 4
  • +2
22 Comments
 
LVL 13

Expert Comment

by:rfwoolf
Comment Utility
Answer below:
0
 
LVL 13

Expert Comment

by:rfwoolf
Comment Utility
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;
  PosOpenBracket := AnsiPos('[', aString); //produces 0 if not found
  PosCloseBracket := AnsiPos(']', aString); //produces 0 if not found
  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;

Open in new window

0
 
LVL 13

Expert Comment

by:rfwoolf
Comment Utility
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

by:wantime
Comment Utility
thanks.

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

0
 
LVL 13

Accepted Solution

by:
rfwoolf earned 125 total points
Comment Utility
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;
  PosOpenBracket := AnsiPos('[', aString); //produces 0 if not found
  PosCloseBracket := AnsiPos(']', aString); //produces 0 if not found
  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

by:wantime
Comment Utility
okay, just use '[ ' and '] '
i will have a try...
0
 
LVL 13

Expert Comment

by:rfwoolf
Comment Utility
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

by:ewangoya
Comment Utility
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

by:rfwoolf
Comment Utility
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

by:wantime
Comment Utility
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

by:epasquier
Comment Utility
> 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;

Open in new window

0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 13

Expert Comment

by:rfwoolf
Comment Utility
>>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

by:epasquier
Comment Utility
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

by:epasquier
epasquier earned 125 total points
Comment Utility
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] );

Open in new window

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

by:epasquier
Comment Utility
oups , bold does not work in code section ( and ) ...
0
 
LVL 32

Assisted Solution

by:ewangoya
ewangoya earned 125 total points
Comment Utility

@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;

Open in new window

0
 
LVL 25

Expert Comment

by:epasquier
Comment Utility
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;

Open in new window

0
 
LVL 24

Assisted Solution

by:jimyX
jimyX earned 125 total points
Comment Utility
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;

Open in new window

0
 
LVL 25

Expert Comment

by:epasquier
Comment Utility
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);

Open in new window

0
 
LVL 25

Expert Comment

by:epasquier
Comment Utility
where is gone my # before 0 ? am I sleeping already or that new editor just swallowed it ?
0
 

Author Comment

by:wantime
Comment Utility
thanks for the different solution and the analyse.
0
 
LVL 13

Expert Comment

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

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

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…

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now