Lazarus

Free Pascal => Beginners => Topic started by: JLWest on December 07, 2018, 01:56:10 am

Title: Strings Again
Post by: JLWest on December 07, 2018, 01:56:10 am
Get this error on line 11. ---> uutils.pas(51,19) Fatal: Syntax error, ";" expected but "(" found

Code: Pascal  [Select][+][-]
  1. function CutLoc (Const aInt : Integer; aStr : String) : Integer;
  2.   Var
  3.    Idx : Integer = 0;
  4.    Lgth : Integer = 0;
  5.    str  : String = '';
  6.    Str2 : String = '';
  7.    begin
  8.      Str2 := aStr;
  9.      Lgth := Length(Str2);
  10.      for IDX := 0 to (Lgth -1) do Begin
  11.        Str := Str2(Idx);
  12.        if Str = ',' and Idx = aInt then  break;
  13.     end;
  14.      Return := IDX;
  15.    end;                                      
Title: Re: Strings Again
Post by: Handoko on December 07, 2018, 02:44:42 am
The line #11 should be:
Code: Pascal  [Select][+][-]
  1.        Str := Str2[Idx];

And line #5 should be:
Code: Pascal  [Select][+][-]
  1.   Str : Char;
Title: Re: Strings Again
Post by: lucamar on December 07, 2018, 03:14:43 am
You don't need Str2: you can use aStr directly. It's a normal value-passed parameter so it can be used as a local var. In fact, most of your code is superfluous.

This is basically what your function does:

Code: Pascal  [Select][+][-]
  1. function CutLoc (Const aInt : Integer; aStr : String) : Integer;
  2. begin
  3.   if (Length(aStr) > aInt) and (aStr[aInt] = ',') then
  4.     Result := aInt
  5.   else
  6.     Result := Length(aStr))
  7. end;
  8.  

BTW, there's another error in your code: line 12:
  if Str = ',' and Idx = aInt then  break;
should be:
  if (Str = ',') and (Idx = aInt) then  break;

Oh! And Strings are indexed from 1, not 0.

Have fun!  ;D
Title: Re: Strings Again
Post by: Handoko on December 07, 2018, 03:24:45 am
I suggest further improvement:

Code: Pascal  [Select][+][-]
  1. function CutLoc(aInt: Integer; const aStr: string): Integer;

The keyword const should be for aStr. Because unmodified string parameters will be optimized if it used with const keyword. And for Integer parameters, the const keyword is not very useful.
Title: Re: Strings Again
Post by: lucamar on December 07, 2018, 03:28:22 am
One could go even further:

Code: Pascal  [Select][+][-]
  1. function CutLoc(
  2.     constref aInt: Integer;
  3.     constref aStr: string): Integer;

Now all things are just pointers ;)
But better use const and let the compiler optimize.
Title: Re: Strings Again
Post by: JLWest on December 07, 2018, 04:58:32 am
@ Lucmar

Your functor always return 65. But maybe I didn't explain the problem very well.

I have a list box with 27,000 + lines.

Each line looks as follows, of course with different values.


 // LAN  ,   YSSY  ,   NZAA  ,   B789_LAN_LATAM  ,  0010  ,   LA800  ,   CC-BGA  ,  0  ,0325

Each line can consist of either 8 or 9 fields delimited by a comma. The spaces between the comma is not uniform. So the record could look like the above orl ike so:


 // LAN,   YSSY,   NZAA ,   B789_LAN_LATAM ,  0010,LA800,   CC-BGA  ,0,0325

I count the commas to get the number of fields and add 1.

If I want the first field I can do a str := Copy(Line,1,3);

But with field 2  I need to know where the first and second comma are located.
Third field the 2 and  and 3rd location of commas.

And so on until I reach 9 or 10 fields.

What I.m trying to do with;
 
 function CutLoc (Const aInt : Integer; aStr : String) : Integer;

is pass the number of the comma I want the location for.

So if I'm looking for the third comma of the following string the function should hand back
16 and the forth comma 34.
So 16 +1 = 17

Cut1 := 17;
Cut2 := 34;
Field4 := (Line,Cut1,cut2);

Field4 = '   B789_LAN_LATAM'
Trim Field4 and I should wind up with Field4 := 'B789_LAN_LATAM'
 
                1                 2                3                4                5                6
123456789012345678901234567890123456789012345678901234567890
LAN,   YSSY,   NZAA ,   B789_LAN_LATAM ,  0010,LA800,   CC-BGA  ,0,0325
 


     
Title: Re: Strings Again
Post by: JLWest on December 07, 2018, 05:08:10 am
I suggest further improvement:

Code: Pascal  [Select][+][-]
  1. function CutLoc(aInt: Integer; const aStr: string): Integer;

The keyword const should be for aStr. Because unmodified string parameters will be optimized if it used with const keyword. And for Integer parameters, the const keyword is not very useful.

Hi Handoko.

I see your still answering beginners questions.
I'm trying to make your answer work but if I understand you.

Basicaly

A : string;
B : String
,
,
,
A := 'ABCDEF';
B:= Copy(A,1,1);
Both A and B are treated as a string;

But B := A(Idx);

the value of A(Idx) is now a Char so if I change B to a Char it should work.
 
Title: Re: Strings Again
Post by: JLWest on December 07, 2018, 05:42:15 am
The line #11 should be:
Code: Pascal  [Select][+][-]
  1.        Str := Str2[Idx];

And line #5 should be:
Code: Pascal  [Select][+][-]
  1.   Str : Char;


Code: Pascal  [Select][+][-]
  1. function CutLoc (Const aStr : String; aInt : Integer) : Integer;
  2.   Var
  3.    Idx : Integer  = 0;
  4.    Lgth : Integer = 0;
  5. //   str  : Char;
  6.   Str : String;
  7.    begin
  8.      Lgth := Length(aStr);
  9.      for IDX := 1 to Length(aStr)  do Begin
  10.       Str := aStr(Idx);
  11.        if Str = ',' and Idx = aInt then  break;
  12.     end;
  13.      Return := IDX;
  14.    end;    

Changing Str to a Char the compiler error is:
uutils.pas(51,15) Error: Incompatible types: got "AnsiString" expected "Char"

Cementing Str out and running str as a string  the compiler error is:
uutils.pas(52,19) Fatal: Syntax error, ";" expected but "(" found

Title: Re: Strings Again
Post by: Handoko on December 07, 2018, 07:09:47 am
A : string;
B : String
,
,
,
A := 'ABCDEF';
B:= Copy(A,1,1);
Both A and B are treated as a string;

But B := A(Idx);

the value of A(Idx) is now a Char so if I change B to a Char it should work.

You wrote it wrong:

Code: Pascal  [Select][+][-]
  1. B := A(Idx);

Below is the corrected version, can you notice the difference?

Code: Pascal  [Select][+][-]
  1. B := A[Idx];

You can use string for B in the line below:

Code: Pascal  [Select][+][-]
  1. B := A[Idx];

But because the result is only a single character string, it is more efficient to use char. String is much expensive compare to char.
Title: Re: Strings Again
Post by: JLWest on December 07, 2018, 07:34:26 am
Yes, Brackets '[' ']' verses '('  ')'.

So I have made your changes to brackets and Char.
It compiled. not I have to test the Logic.

Thank You
Title: Re: Strings Again
Post by: howardpc on December 07, 2018, 09:41:40 am
I think you're looking for a function something like this:
Code: Pascal  [Select][+][-]
  1. function GetFieldNo(const aText: String; aFieldNo: Integer; out fieldCount: Integer): String;
  2. const
  3.   Comma = ',';
  4. type
  5.   TPosArray = array[1..9] of Integer;
  6. var
  7.   text: String;
  8.   arr:  TPosArray;
  9.   i: Integer;
  10. begin
  11.   fieldCount := 0;
  12.   text := Trim(aText);
  13.   arr := Default(TPosArray);
  14.   if (Length(text) < 2) or (Pos(Comma, text) = 0) or (aFieldNo < 1) then
  15.     Exit('');
  16.   for i := 1 to Length(text) do
  17.     if text[i] = Comma then
  18.       begin
  19.         Inc(fieldCount);
  20.         arr[fieldCount] := i;
  21.       end;
  22.   if aFieldNo > Succ(fieldCount) then
  23.     Exit('');
  24.   case aFieldNo = 1 of
  25.     True:  Result := Trim(Copy(text, 1, Pred(arr[1])));
  26.     False: begin
  27.              if aFieldNo = Succ(fieldCount) then
  28.                Result := Trim(Copy(text, Succ(arr[fieldCount]), Length(text)))
  29.              else Result := Trim(Copy(text, Succ(arr[Pred(aFieldNo)]), Pred(arr[aFieldNo] - arr[Pred(aFieldNo)])));
  30.            end;
  31.   end;
  32.   Inc(fieldCount);
  33. end;
Title: Re: Strings Again
Post by: JLWest on December 07, 2018, 06:19:51 pm
@Howardpc

Yes It is what I'm trying to do I think. I can follow the solution a little bit. But will have to research some of the statements.   "arr := Default(TPosArray);" Not sure what Default dose for you. Researching on google is pretty tedious for me right now.

But if I get the print  big enough I can sometimes figure this out.

Title: Re: Strings Again
Post by: howardpc on December 07, 2018, 06:24:45 pm
Default() is a recently introduced compiler intrinsic that initialises any variable to zero/nil/null value(s) as appropriate depending on the variable's type.
Earlier code would have done this by calling FillChar() or FillWord(),  or by explicitly assigning zero to each record field or array element etc.
Title: Re: Strings Again
Post by: garlar27 on December 07, 2018, 06:56:12 pm
Something like this CSV (http://wiki.lazarus.freepascal.org/CSV) might be useful...
Title: Re: Strings Again
Post by: Bart on December 07, 2018, 07:23:21 pm
Did anybody suggest using TStringList.CommaText?

Bart
Title: Re: Strings Again
Post by: garlar27 on December 07, 2018, 10:39:46 pm
Did anybody suggest using TStringList.CommaText?
Didn't know about it  :o
Title: Re: Strings Again
Post by: jamie on December 07, 2018, 11:45:22 pm
He needs a parsing "GET" function, one that ignores spaces as it builds a captured string until a comma is hit and
thus remembers where it was the last time for the next read of the string.

For example

Var
 X:Integer;
 S:Sting;
Begin
 X ;= 0; first initial setting for the line.

 S:= GET(X);
 While S <> '' Do begin
  begin
    // do something with S;
   S := GET(X); // the Get function will auto increment for the next location.
 End;


The GET function may also consider accepting spaces for the content if there was a quote started.

I am sure with all the powers to be here, someone can hack out a function like that..  :)

 
Title: Re: Strings Again
Post by: JLWest on December 08, 2018, 12:15:25 am
Have been trying to make the function supplied by Handoko but it only eturns the first field.

It close but I can't figure out why it won't return the second or fourth field.

Attached is a self contained demo using the function.

Code: Pascal  [Select][+][-]
  1.      function TForm1.GetFieldNo(const aText: String; aFieldNo: Integer; out fieldCount: Integer): String;
  2.     const
  3.       Comma = ',';
  4.     type
  5.       TPosArray = array[1..9] of Integer;
  6.     var
  7.       txt: String;
  8.       arr:  TPosArray;
  9.       i: Integer;
  10.     begin
  11.       fieldCount := 0;
  12.       txt := Trim(aText);
  13.       arr := Default(TPosArray);
  14.       if (Length(txt) < 2) or (Pos(Comma, txt) = 0) or (aFieldNo < 1) then
  15.         Exit('');
  16.       for i := 1 to Length(txt) do
  17.         if txt[i] = Comma then
  18.           begin
  19.             Inc(fieldCount);
  20.             arr[fieldCount] := i;
  21.           end;
  22.       if aFieldNo > Succ(fieldCount) then
  23.         Exit('');
  24.       case aFieldNo = 1 of
  25.         True:  Result := Trim(Copy(txt, 1, Pred(arr[1])));
  26.         False: begin
  27.                  if aFieldNo = Succ(fieldCount) then
  28.                    Result := Trim(Copy(text, Succ(arr[fieldCount]), Length(text)))
  29.                  else Result := Trim(Copy(text, Succ(arr[Pred(aFieldNo)]), Pred(arr[aFieldNo] - arr[Pred(aFieldNo)])));
  30.                end;
  31.       end;
  32.       Inc(fieldCount);
  33.     end;                        

 
Title: Re: Strings Again
Post by: wp on December 08, 2018, 12:37:58 am
JLWest, did you consider Bart's suggestion with TStringList in reply #14? It works perfectly:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. uses
  4.   Classes;
  5.  
  6. const
  7.   s =  'LAN  ,   YSSY  ,   NZAA  ,   B789_LAN_LATAM  ,  0010  ,   LA800  ,   CC-BGA  ,  0  ,0325';
  8.  
  9. var
  10.   i: Integer;
  11.   L: TStringList;
  12. begin
  13.   L := TStringList.Create;
  14.   try
  15.     L.CommaText := s;
  16.     WriteLn(L.count, ' items:');
  17.     for i:=0 to L.Count-1 do
  18.       WriteLn('"', L[i], '"');
  19.   finally
  20.     L.Free;
  21.   end;
  22.  
  23.   ReadLn;
  24. end.

Output (the quotes were added to show whether the spaces are trimmed correctly):
Quote
9 items:
"LAN"
"YSSY"
"NZAA"
"B789_LAN_LATAM"
"0010"
"LA800"
"CC-BGA"
"0"
"0325"
Title: Re: Strings Again
Post by: jamie on December 08, 2018, 12:46:52 am
as WP pointed out, that will work fine but since I've already banged out some code I just have to post it here  :)
Code: Pascal  [Select][+][-]
  1. { TForm1 }
  2. Function GET(Const InputStr:String;Var X :Integer):String;
  3. begin
  4.   Result := '';
  5.   Repeat
  6.    Inc(X); // move to next char;
  7.    If X > Length(Inputstr) Then
  8.     Begin
  9.      Dec(X);
  10.      Exit;// done;
  11.     End;
  12.    If Not (InputStr[X] in[' ',',']) Then Result := Result+inputstr[x];
  13.   Until InputStr[X] = ',';
  14. end;
  15.  
  16. procedure TForm1.Button1Click(Sender: TObject);  //Test run..
  17. Var
  18.   A:Array[1..20] of string;
  19.   X,Aindex:Integer;
  20.   S,B:String;
  21. begin
  22.    X := 0; Aindex := 0;
  23.    S:= 'Some_Test_String , To_See_if_it_Works  ,END'; //Fake string for test.
  24.    B := Get(S,X);  //Get the first one;
  25.    While B <> '' do
  26.     Begin
  27.       Inc(AIndex);    //Number of entries of string;
  28.       A[AIndex] := B;  //Store each one in a string by itself;
  29.       Caption := Caption+B; //Use CAPTION as a repository and debug display.
  30.       B := Get(S,X);  //Get next;
  31.     end;
  32. // at this point the aIndex should be 1 or more, if 0 then nothing in string;
  33. // Also after each read, X will be resting at a ',' or length of input string, which means no more.
  34. end;                                                                                  
  35.  
Title: Re: Strings Again
Post by: lucamar on December 08, 2018, 01:03:04 am
This one should work too, but I didn't test it :-[

Code: Pascal  [Select][+][-]
  1. function CutLoc(aInt: Integer; const aStr: string): Integer;
  2. var
  3.   Loc, Count: Integer;
  4. begin
  5.   Count := 0;
  6.   Loc := 1;
  7.   repeat
  8.     { Find the next "," from position Loc }
  9.     Loc := PosEx(',', aStr, Loc);
  10.     { Found one? update the count of commas
  11.       and the next starting position}
  12.     if Loc > 0 then begin
  13.       Inc(Count);
  14.       Inc(Loc);
  15.     end;
  16.   until (Count = aInt) or (not (Loc in [1..Length(aStr)]));
  17.   if Count <> aInt then
  18.     { Not enough commas! Do something }
  19.   else
  20.     Result := Loc;
  21. end;
  22.  
Title: Re: Strings Again
Post by: JLWest on December 08, 2018, 08:53:11 am
JLWest, did you consider Bart's suggestion with TStringList in reply #14? It works perfectly:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. uses
  4.   Classes;
  5.  
  6. const
  7.   s =  'LAN  ,   YSSY  ,   NZAA  ,   B789_LAN_LATAM  ,  0010  ,   LA800  ,   CC-BGA  ,  0  ,0325';
  8.  
  9. var
  10.   i: Integer;
  11.   L: TStringList;
  12. begin
  13.   L := TStringList.Create;
  14.   try
  15.     L.CommaText := s;
  16.     WriteLn(L.count, ' items:');
  17.     for i:=0 to L.Count-1 do
  18.       WriteLn('"', L[i], '"');
  19.   finally
  20.     L.Free;
  21.   end;
  22.  
  23.   ReadLn;
  24. end.

Output (the quotes were added to show whether the spaces are trimmed correctly):
Quote
9 items:
"LAN"
"YSSY"
"NZAA"
"B789_LAN_LATAM"
"0010"
"LA800"
"CC-BGA"
"0"
"0325"

I have tried for a couple of days to get this to work. And I have trouble with each suggestion.
I have got the line parsed for 7 of the 9 fields , but the 8th and 9th fields give me a lot of trouble. A record will always have 8 fields but the 9 field is optional.

Now I'm looking at the TStringList solution.

It looks simple.
Will something like this workl

I read a record from the file: TFRecord

L := TStringList.Create;
 try
    L.CommaText := TFRecord;
for i:=0 to L.Count-1 do Begin <----------------- going thru the list of fields
      Field := L;                 <-------------------- Field is a String             
     ValidField :=  VerifyField(Field); <------------ValidField is a Boolean
end; <----------------------------------------------End of list
  finally
    L.Free;

I'll try this tomorrow.
Title: Re: Strings Again
Post by: wp on December 08, 2018, 10:04:10 am
L := TStringList.Create;
 try
    L.CommaText := TFRecord;
for i:=0 to L.Count-1 do Begin <----------------- going thru the list of fields
      Field := L;                 <-------------------- Field is a String             
     ValidField :=  VerifyField(Field); <------------ValidField is a Boolean
end; <----------------------------------------------End of list
  finally
    L.Free;
This actually is correct although the line "Field := L" looks wrong. This is because the forum software swallows the character i enclosed by square brackets. I am repeating your code here using the index j instead of i to demonstrate that it does not contain a line "Field := L"

L := TStringList.Create;
 try
    L.CommaText := TFRecord;
for j:=0 to L.Count-1 do Begin <----------------- going thru the list of fields
      Field := L[j];                 <-------------------- Field is a String             
     ValidField :=  VerifyField(Field); <------------ValidField is a Boolean
end; <----------------------------------------------End of list
  finally
    L.Free;
end;

In general it would be better to enclose your source code by [code ] tags (Select it and click on the # button above the edit field):
Code: Pascal  [Select][+][-]
  1. L := TStringList.Create;
  2.  try
  3.     L.CommaText := TFRecord;
  4. for i:=0 to L.Count-1 do Begin <----------------- going thru the list of fields
  5.       Field := L[i];                 <-------------------- Field is a String            
  6.      ValidField :=  VerifyField(Field); <------------ValidField is a Boolean
  7. end; <----------------------------------------------End of list
  8.   finally
  9.     L.Free;
  10.   end;
  11.  
Title: Re: Strings Again
Post by: JLWest on December 08, 2018, 06:26:18 pm
Yea, I noticed I could't get the '[' ']' to display.

I'm about to build a little test program to try TStringList. Let you know.

Thanks.
Title: Re: Strings Again
Post by: JLWest on December 08, 2018, 08:26:39 pm
Code: Pascal  [Select][+][-]
  1.  function TForm1.ProcessOneRecord(aTFrec : String) : XferRecord;
  2.   Var
  3.     Lst : TStringList;
  4.     XFerRec : XferRecord;
  5.     TFRecord : String = '';
  6.     Field : String;
  7.     Flds : Integer;
  8.     Idx : Integer;
  9.   Begin
  10.    Lst := TStringList.Create;
  11.   try
  12.      Lst.CommaText := aTFrec;
  13.      Flds := Lst.Count;
  14.      XFerRec.TxtRec:= aTFrec;
  15.      XFerRec.Fields:= Flds;
  16.  
  17.     for Idx:= 0 to Lst.Count-1 do begin
  18.      Field :=  Lst[Idx];
  19.      XFerRec := UpdateXFerRecord(Field,Idx,XFerRec);
  20.      ShowField(Field);
  21.     end;
  22.   finally
  23.     Lst.Free;
  24.     Result := XFerRec;
  25.   end;
  26.  end;                              

Got this working great.
 
It's the "Lst.CommaText := aTFrec;" that does all the heavy lifting I think.

Have to try it on a record that looks like the following:

  'LAN,YSSY,NZAA,B789_LAN_LATAM, 0010,LA800,CC-BGA,0'

WOW.

Thanks

Title: Re: Strings Again
Post by: garlar27 on December 10, 2018, 04:48:24 pm
Maybe it's a little late but
Have you tried something like this (though I donĀ“t know how it will work if one field is missing):

Code: Pascal  [Select][+][-]
  1. uses CsvDocument;
  2.  
  3. type
  4.    TFlight = record
  5.       Field01: string;
  6.       Field02: string;
  7.       Field03: string;
  8.       Field04: string;
  9.       Field05: string;
  10.       Field06: string;
  11.       Field07: string;
  12.       Field08: string;
  13.       Field09: string;
  14.    end;
  15.  
  16. procedure ProcessData(const AFile: string);
  17. var
  18.    CSVDocument : TCSVDocument;
  19.    ind: Integer;
  20.    AFlight: TFlight;
  21. begin
  22.    CSVDocument := nil;
  23.    try
  24.       CSVDocument := TCSVDocument.Create;
  25.  
  26.       CSVDocument.LoadFromFile(AFile);
  27.       for ind := 1 to CSVDocument.RowCount - 1 do begin
  28.          AFlight.Field01 := Trim(CSVDocument.Cells[0,ind]);
  29.          AFlight.Field02 := Trim(CSVDocument.Cells[1,ind]);
  30.          AFlight.Field03 := Trim(CSVDocument.Cells[2,ind]);
  31.          AFlight.Field04 := Trim(CSVDocument.Cells[3,ind]);
  32.          AFlight.Field05 := Trim(CSVDocument.Cells[4,ind]);
  33.          AFlight.Field06 := Trim(CSVDocument.Cells[5,ind]);
  34.          AFlight.Field07 := Trim(CSVDocument.Cells[6,ind]);
  35.          AFlight.Field08 := Trim(CSVDocument.Cells[7,ind]);
  36.          AFlight.Field09 := Trim(CSVDocument.Cells[8,ind]);
  37.          // Do something with AFlight
  38.       end; {<--- del for ind }
  39.    finally
  40.       FreeAndNil(CSVDocument);
  41.    end;
  42. end;
  43.  
Title: Re: Strings Again
Post by: Almir.Bispo on December 10, 2018, 06:20:58 pm
You can use this.The CSV Comp DB with CSV-Pascal Library (open source).You  must prefer semi-colon delimited file
free get on http://adltecnologia.blogspot.com.br (http://adltecnologia.blogspot.com.br)
Look PowerFul Exemple below

Code: Pascal  [Select][+][-]
  1.  //ILDE-Pascal (open-source)
  2. {$i ...\CQL_TO_PASCAL.txt}
  3. procedure MycsvONDB;
  4. var myresult:Tstringlist;
  5.     cursor,db,id_table:string;
  6. begin
  7. myresult:=Tstrinlist.create;
  8. db :='c:\mydb';
  9. id_table :='MyTable'; //csv table
  10. //add CQL Command to run
  11. cursor :=  default_delimiter(db,id_table,',');//change ',' by default (';')  delimiter
  12. cursor := set_underline(db,id_table);         //change space key by underline (no data lost)
  13. //maybe ou need to list all data on column index 2 (or other)
  14. cursor := field_list(db,id_table,'Any',2); //all data is listed on MyTable.que
  15. CSV_COMP_DB_execute(cursor);//DB execution
  16. //now just load result
  17. while not ( fileexists('lock.inf') )do begin {wait start} end;
  18. while  ( fileexists('lock.inf') ) do begin {wait execution} end;
  19. //load
  20. myresult.loadfromfile(application.location + id_table + '.que');
  21. Memo1.text := myresult.text;
  22. myresult.free;
  23.  
  24. end;
  25.  

The exemple must  runs on same path of CSV Comp DB (Desktop version)
You can do Everthing with csv (Nosql Cloud technology,IOT,E-Commerce).
Everthing Free and Open
TinyPortal © 2005-2018