Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win

x
?
Solved

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

Posted on 2010-11-18
22
Medium Priority
?
650 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 8
  • 7
  • 4
  • +2
22 Comments
 
LVL 13

Expert Comment

by:rfwoolf
ID: 34164932
Answer below:
0
 
LVL 13

Expert Comment

by:rfwoolf
ID: 34164935
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
ID: 34164965
Oh shucks, you may get an access violation because it should be:
  for i := 0 to length(A) -1 do
(note the -1)
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 

Author Comment

by:wantime
ID: 34165027
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 500 total points
ID: 34165056
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
ID: 34165066
okay, just use '[ ' and '] '
i will have a try...
0
 
LVL 13

Expert Comment

by:rfwoolf
ID: 34165093
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:Ephraim Wangoya
ID: 34165109
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
ID: 34165131
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
ID: 34165351
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
ID: 34168620
> 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
 
LVL 13

Expert Comment

by:rfwoolf
ID: 34168641
>>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
ID: 34169061
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 500 total points
ID: 34169153
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
ID: 34169162
oups , bold does not work in code section ( and ) ...
0
 
LVL 32

Assisted Solution

by:Ephraim Wangoya
Ephraim Wangoya earned 500 total points
ID: 34169679

@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
ID: 34169761
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 500 total points
ID: 34170068
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
ID: 34170102
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
ID: 34170107
where is gone my # before 0 ? am I sleeping already or that new editor just swallowed it ?
0
 

Author Comment

by:wantime
ID: 34171419
thanks for the different solution and the analyse.
0
 
LVL 13

Expert Comment

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

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
This course is ideal for IT System Administrators working with VMware vSphere and its associated products in their company infrastructure. This course teaches you how to install and maintain this virtualization technology to store data, prevent vuln…
Want to learn how to record your desktop screen without having to use an outside camera. Click on this video and learn how to use the cool google extension called "Screencastify"! Step 1: Open a new google tab Step 2: Go to the left hand upper corn…
Suggested Courses

610 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