Lazarus

Free Pascal => Beginners => Topic started by: matandked on February 03, 2017, 06:04:49 pm

Title: Save and write whole arrays of numbers to the text files
Post by: matandked on February 03, 2017, 06:04:49 pm
I just read article on wiki about handling files (http://wiki.freepascal.org/File_Handling_In_Pascal).

It seems that saving arrays to a binary files is quite easy (http://wiki.freepascal.org/File_Handling_In_Pascal#Binary_files)
However, I would like to read/write whole arrays from/to text/CSV files.

I can do this with for-loop statements (as I do in my code below which saves array to file), but I wonder if there's any better/simpler way of doing this in Free Pascal/Lazarus?
Maybe there's a counterpart of write.csv R! function (http://rprogramming.net/write-csv-in-r/)?


Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   {$IFDEF UNIX}{$IFDEF UseCThreads}
  7.   cthreads,
  8.   {$ENDIF}{$ENDIF}
  9.   Classes, Sysutils;
  10.  
  11. const
  12.   C_FNAME = 'Array_Saved_to_TextFile.txt';
  13.   // separate by tab:
  14.   separator = Char(9);
  15. var
  16.   // I would like to save this array to text file
  17.   // separated by tabs or any other character
  18.   myArray: array [1..2,1..3] of Real =
  19.     ((0.5, 2.4, 1.2), (3.4, 5.3, 6.5));
  20.   slInfo: TStringList;
  21.   row, column: Integer;
  22.   line : String;
  23.  
  24. begin
  25.   If not FileExists(C_FNAME) Then
  26.     begin
  27.  
  28.       // I wonder if there's any simpler/better solution
  29.       // For example if in Free Pascal there's counterpart of
  30.       // R! write.table function
  31.       /////////////////////////////////////////////////////////
  32.       slInfo := TStringList.Create;
  33.       For row:=1 to 2 do
  34.           begin
  35.             line := '';
  36.             For column:=1 to 3 do
  37.                 line := line + FloatToStr(myArray[row, column]) + separator;
  38.           Delete (line,Length(Line),1);
  39.           slInfo.Add(line);
  40.           end;
  41.  
  42.       slInfo.SaveToFile(C_FNAME);
  43.       slInfo.Free;
  44.       /////////////////////////////////////////////////////////
  45.  
  46.  
  47.       WriteLn('Array has been saved in file: ' + Char(39) + C_FNAME + char(39));
  48.     end
  49.   else
  50.       begin
  51.         WriteLn('Following file already exists: ' + Char(39) + C_FNAME + Char(39));
  52.       end;
  53.   ReadLn;
  54. end.
  55.  
Title: Re: Save and write whole arrays of numbers to the text files
Post by: Thaddy on February 03, 2017, 06:11:11 pm
If you insist on storing the values in a human readable format, I am afraid, that your current effort comes close to a good solution. Thumbs up!
If that is not necessary, there are several options to speed things up, big time.

Can you explain - in a better way - why you need the human readable format?
Title: Re: Save and write whole arrays of numbers to the text files
Post by: lainz on February 03, 2017, 06:21:30 pm
Maybe to be edited in a third party software, Excel or LibreOffice Calc

What I learned in university about CSV is that you need to specify (not necessary at all but useful) the width and height of the array, so you can call SetLength at the beginning, then load it line by line, instead of loading the entire text file in memory. If the file is not big you can use TStringList and for each N line do a new temporary TStringList and add the N line as delimitedtext or commatext, I don't remember wich one. Then you can access the horizontal data with an index of the temporary TStringList.

To save you already have a code, is the most simple part, you can specify here the width and height of the array (not needed if you use a TStringList, since it split the data with commatext or delimitedtext at loading time).

Another thing I've learned is that is a wrong code:

Code: Pascal  [Select][+][-]
  1. For row:=1 to 2 do
  2.  
  3. ...
  4.  
  5. For column:=1 to 3 do
  6.  
Always refer to the array length, don't use any numeric constants 'magic numbers'. If you change the data length you will not need to change these numbers later.
Title: Re: Save and write whole arrays of numbers to the text files
Post by: Handoko on February 03, 2017, 06:30:09 pm
Yes, 'magic numbers' should be avoid unless you are sure it won't change. So the better code is:

Code: Pascal  [Select][+][-]
  1. const
  2.   RowCount = 2;
  3.   ColumnCount = 3;
  4.  
  5. var
  6.   myArray: array [1..RowCount, 1..ColumnCount] of Real = ...
  7.  
  8. ...
  9. for Row := 1 to RowCount do
  10. ...
  11.   for Column := 1 to ColumnCount do
  12. ...
...
Title: Re: Save and write whole arrays of numbers to the text files
Post by: matandked on February 03, 2017, 06:44:15 pm
Yes, 'magic numbers' should be avoid unless you are sure it won't change. So the better code is:

Code: Pascal  [Select][+][-]
  1. const
  2.   RowCount = 2;
  3.   ColumnCount = 3;
  4.  
  5. var
  6.   myArray: array [1..RowCount, 1..ColumnCount] of Real = ...
  7.  
  8. ...
  9. for Row := 1 to RowCount do
  10. ...
  11.   for Column := 1 to ColumnCount do
  12. ...
...


I wasn't aware that it is possible to do that in FreePascal - I expected that I will be able to get them in different way for example: myArray.GetNumberOfRows
Thank you!!!


If you insist on storing the values in a human readable format, I am afraid, that your current effort comes close to a good solution. Thumbs up!
If that is not necessary, there are several options to speed things up, big time.

Can you explain - in a better way - why you need the human readable format?

Thank you for a quick response.

The only reason is that end users requested to have output in Excel and text file/human readable formats, because they're familiar with them.

Generally speaking I was asked to write a program that:


Off topic
I thought about using FPSpreadsheet (http://wiki.freepascal.org/FPSpreadsheet) to read data from Excel file.
I'm not sure if there's any ready-to-use function to read Excel range of cells as Free Pascal array (in such case I will need to write another loop again to iterate over cells in range  >:D).
Anyway, I think that it will be better way to firstly read all numbers as arrays and then performing operations on them (am I correct or should I change all my assumptions from the beginning  ::)
Besides I haven't checked yet if Free Pascal has any library for table/matrix operations (so that I will be able to write shorter code such as in Octave/Matlab), but I assume that yes - it seems to be a cool language from the new user perspective  8-)
Title: Re: Save and write whole arrays of numbers to the text files
Post by: User137 on February 04, 2017, 11:24:08 am
You don't have to use consts if you don't want to. High() and Low() returns the array range. These 2 ways do the same
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. var row, column: integer;
  3. begin
  4.   //for Row := 1 to RowCount do
  5.     //for Column := 1 to ColumnCount do begin
  6.   for Row := 1 to high(myArray) do
  7.     for Column := 1 to high(myArray[1]) do begin
  8.       memo1.Lines.Add(format('%d, %d',[row, column]));
  9.     end;
  10. end;
I didn't use low() because i assumed it starts from 1. In most cases i prefer starting all arrays from 0 though, because that's what machines tend to like most.
Title: Re: Save and write whole arrays of numbers to the text files
Post by: maurobio on September 25, 2021, 11:43:46 pm
Dear ALL,

Here is my humble contribution to the question presented by @matendked:

Code: Pascal  [Select][+][-]
  1. program test;
  2.  
  3. {$APPTYPE CONSOLE}
  4.  
  5. uses Classes, SysUtils, StrUtils;
  6.  
  7. type
  8.         TMatrix = array of array of double;
  9.        
  10. var
  11.         n, m, i, j: integer;
  12.         arr: TMatrix;
  13.        
  14. procedure SaveArray(filename: string; n, m: integer; arr: TMatrix; separator: char; format: char);
  15. var
  16.   slInfo: TStringList;
  17.   row, col: integer;
  18.   line: string;
  19. begin
  20.   slInfo := TStringList.Create;
  21.   for row := 0 to n - 1 do
  22.   begin
  23.     line := '';
  24.     for col := 0 to m - 1 do
  25.         begin
  26.       if format = 'i' then     
  27.                 line := line + FloatToStrF(arr[row, col], ffFixed, 5, 0) + separator
  28.           else if format = 'f' then
  29.                 line := line + FloatToStrF(arr[row, col], ffFixed, 7, 2) + separator;
  30.         end;  
  31.     Delete(line, Length(line), 1);
  32.     slInfo.Add(line);
  33.   end;
  34.   slInfo.SaveToFile(filename);
  35.   slInfo.Free;
  36. end;   
  37.  
  38. procedure ReadArray(filename: string; separator: char; var a: TMatrix; var n, m: integer);
  39. var
  40.         SL, TL: TStringList;
  41.         I, J: integer;
  42. begin
  43.   SL := TStringList.Create;
  44.   SL.LoadFromFile(filename);
  45.   n := SL.Count;
  46.   m := SL[0].CountChar(separator) + 1;
  47.   SetLength(a, n, m);
  48.   for I := 0 to n - 1 do
  49.   begin
  50.         TL := TStringList.Create;
  51.         TL.Delimiter := separator;
  52.     TL.DelimitedText := SL[I];
  53.         for J := 0 to m - 1 do
  54.                 a[I, J] := StrToInt(TL[J]);
  55.     TL.Free;
  56.   end;
  57.   SL.Free;
  58. end;
  59.  
  60. begin
  61.         ReadArray('test.csv', ',', arr, n, m);
  62.         WriteLn(n, ' rows, ', m, ' cols');
  63.         for i := 0 to n - 1 do
  64.         begin
  65.                 for j := 0 to m - 1 do
  66.                         Write(arr[i, j]:5:0);
  67.                 WriteLn;
  68.         end;
  69.         SaveArray('test2.csv', n, m, arr, ';', 'i');
  70. end.
  71.  
Procedures ReadArray and SaveArray are inspired by the savetxt and loadtxt functions from Python Numpy library.

Hope it helps.

With best wishes,
Title: Re: Save and write whole arrays of numbers to the text files
Post by: Bart on September 26, 2021, 11:21:03 am
In LazuTils there is a unit called lcsvutils that proviedes methods for reading and writing CSV documents.

Also fpc should have CsvDocument since version 3.0 (according to this wikipage (https://wiki.freepascal.org/CsvDocument)).

FpSpreadSheet is your best bet at writing excel files, you don't need a working Excel on the system to create or read the files (no COM).

I have a small subset of write functions to Excel version 2.0 (text fields only), which might suffice.

Bart
Title: Re: Save and write whole arrays of numbers to the text files
Post by: Thaddy on September 26, 2021, 12:51:30 pm
I didn't use low() because i assumed it starts from 1.
That is a wrong assumption. Dynamic arrays always start at zero.

Aside: you can actually flatten the array loops into a single one by multiplication of n and m.
That should be a little faster.
Title: Re: Save and write whole arrays of numbers to the text files
Post by: BobDog on September 26, 2021, 04:26:48 pm

With 'msvcrt.dll' (I think just 'c' in linux), you can block load and save
Code: Pascal  [Select][+][-]
  1. program matrixtofile;
  2.  
  3.     function fopen (p1:pchar; p2:pchar):pointer ; cdecl external 'msvcrt.dll' name 'fopen';
  4.     function fread (p1:pointer;i1:integer;i2:integer;_FILE: pointer):integer ; cdecl external 'msvcrt.dll' name 'fread';
  5.     function fwrite (p1:pointer;i1:integer;i2:integer;_FILE: pointer):integer ; cdecl external 'msvcrt.dll' name 'fwrite';
  6.     function fclose (_FILE:pointer):integer   cdecl external 'msvcrt.dll' name 'fclose';
  7.  
  8.  
  9.   type matrix=array of array of real;
  10.  
  11.  
  12.        function DeleteFile(filename: string): Boolean;
  13.          var
  14.          F: file;
  15.          begin
  16.          Assign(F, filename);
  17.         {$I-}
  18.         Erase(F);
  19.        {$I+}
  20.        DeleteFile:=IOResult=0;
  21.        end;
  22.  
  23.         procedure savefile(content:matrix;filename:pchar);
  24.        var
  25.        w:pchar='wb';
  26.        fp:pointer;
  27.        begin
  28.        fp:=fopen(filename,w);
  29.        if fp = nil then
  30.        begin
  31.        writeln( 'Unable to save  ',filename);
  32.        exit
  33.        end;
  34.        fwrite(pointer(content), 1, sizeof(content)*length(content), fp);
  35.         fclose(fp);
  36.        end;
  37.  
  38.  
  39.        procedure loadfile(content:matrix;filename:pchar);
  40.        var
  41.        w:pchar='rb';
  42.        fp:pointer;
  43.        begin
  44.        fp:=fopen(filename,w);
  45.        if fp = nil then
  46.        begin
  47.        writeln( 'Unable to open  ',filename);
  48.        exit
  49.        end;
  50.        fread(pointer(content), 1, sizeof(content)*length(content), fp);
  51.         fclose(fp);
  52.        end;
  53.  
  54.  
  55.    var
  56.    i,j:int32;
  57.    filename:pchar='array.dat';
  58.     m: matrix;
  59.     ret:matrix;
  60.  
  61.  
  62.    begin
  63.     SetLength(m,3,4);
  64.  
  65.      m[1,1]:=0.5;
  66.      m[1,2]:=2.4 ;
  67.      m[1,3]:=1.2;
  68.  
  69.      m[2,1]:=3.4;
  70.      m[2,2]:=5.3 ;
  71.      m[2,3]:=6.5;
  72.  
  73.     writeln('To file');
  74.     for i:=1 to 2 do
  75.       for j:=1 to 3 do
  76.       begin
  77.       write(m[i,j],',');
  78.       if (j=3) then writeln;
  79.       end;
  80.  
  81.     savefile(m,filename);
  82.  
  83.  
  84.      SetLength(ret,3,4);
  85.  
  86.       loadfile(ret,filename);
  87.  
  88.         writeln;
  89.        writeln('from file');
  90.       for i:=1 to 2 do
  91.       for j:=1 to 3 do
  92.       begin
  93.       write(ret[i,j],',');
  94.       if (j=3) then writeln;
  95.       end;
  96.       writeln;
  97.       writeln(deletefile(filename));
  98.    readln;
  99.  
  100.    end.
  101.  
Title: Re: Save and write whole arrays of numbers to the text files
Post by: Bart on September 26, 2021, 09:19:56 pm
With 'msvcrt.dll' (I think just 'c' in linux), you can block load and save

No need for all that, there is old fashioned BlockRead and BlockWrite.
Or use a TFileStream.

Bart
Title: Re: Save and write whole arrays of numbers to the text files
Post by: BobDog on September 26, 2021, 09:46:03 pm

Hello Bart
I don't see any block read/write or Tfilestream examples here, they all loop through a matrix.
Yes, my contribution is a bit out of the pascal box, but it is an answer, of sorts, to matendked's first post - (I would like to read/write whole arrays from/to text/CSV files).


Title: Re: Save and write whole arrays of numbers to the text files
Post by: wp on September 26, 2021, 11:13:18 pm
Here is a tested example for writing/reading a matrix by means of BlockWrite/BlockRead.

I think it is very important to understand that there is a big difference between static and dynamic matrices. In a static matrix, the dimension is given by the type at compile time (array[0..1, 0..2] of Double for a 2x3 matrix), and in this case the matrix simply can be written by calling BlockWrite(F, M, SizeOf(M)). This works because this kind of matrix is a contiguous array of double values

A dynamic matrix which is dimensioned at run time, e.g. by calling SetLength(M, 2, 3). Essentially M is pointer. It points to another array of pointers which in turn point to an array of values in each row. The row values do occupy contiguous memory blocks, but a row is not immediately adjacent to the previous row! Therefore writing (and reading) is more complicated - this cannot be avoided:
- First: Write the number of rows and columns
- Second: Iterate through the rows. The first value in each row is M[row, 0]. Use this as argument in the BlockWrite command. And the number of bytes to be written is the number of columns times the size of a double.

Reading follows the same steps, only replacing BlockWrite by BlockRead.

Ah, and of course, I must mention that this memory map is valid only when the first index of the matrix is the row index, and the second index is the column index  - just like in mathematics.

And finally it must be mentioned that dynamic array ALWAYS begin at index 0.  This is against math convention which begins at 1. For sake of your mental sanity, never attempt to allocate one excess matrix element per row and per column because you want to begin your matrix with index 1. Pretty soon your head will be spinning because you never know whether your matrices are correctly allocated. And Lazarus is not forgiving any mistake here.

Code: Pascal  [Select][+][-]
  1. program matrixtofile;
  2.  
  3. uses
  4.   SysUtils;
  5.  
  6. type
  7.   { It is assumed that the first index is the row index, the second index is the
  8.     column index
  9.  
  10.       ( m00 m01 m02 m03 m04 ... )
  11.       ( m10 m11 m12 m13 m14 ... )
  12.       ( ...                     )      }
  13.   TMatrix = array of array of Double;
  14.   TMatrix_2x3 = packed array[0..1, 0..2] of Double;
  15.  
  16. procedure SaveMatrixToFile(const M: TMatrix; AFileName: String);
  17. var
  18.   F: File;
  19.   nr, nc, r: Integer;
  20. begin
  21.   if FileExists(AFileName) then
  22.     DeleteFile(AFileName);
  23.  
  24.   AssignFile(F, AFileName);
  25.   Rewrite(F);
  26.   // Write the number of rows first
  27.   nr := Length(M);
  28.   BlockWrite(F, nr, SizeOf(nr));
  29.   // Write the number of columns
  30.   nc := Length(M[0]);
  31.   BlockWrite(F, nc, SizeOf(nc));
  32.   // Write rows - there's number after number in the rows (but not row by row!)
  33.   for r := 0 to nr-1 do
  34.     // Per row, there are nc columns, each one has the size of a Double
  35.     BlockWrite(F, M[r, 0], nc*SizeOf(Double));
  36.   CloseFile(F);
  37. end;
  38.  
  39. procedure SaveMatrix2x3ToFile(const M: TMatrix_2x3; AFileName: String);
  40. var
  41.   F: File;
  42. begin
  43.   if FileExists(AFileName) then
  44.     DeleteFile(AFileName);
  45.  
  46.   AssignFile(F, AFileName);
  47.   Rewrite(F);
  48.   // Write the matrix in one single call. This is possible because the numbers
  49.   // follow one each other. The first number is M[0,0].
  50.   BlockWrite(F, M, SizeOf(TMatrix_2x3));
  51.   CloseFile(F);
  52. end;
  53.  
  54. procedure LoadMatrixFromFile(var M: TMatrix; AFileName: String);
  55. var
  56.   F: File;
  57.   nr: Integer = 0;
  58.   nc: Integer = 0;
  59.   r: Integer;
  60.   n: Integer;
  61. begin
  62.   AssignFile(F, AFileName);
  63.   Reset(F);
  64.   // Read number of rows
  65.   BlockRead(F, nr, SizeOf(nr));
  66.   // Read number of cols
  67.   BlockRead(F, nc, SizeOf(nc));
  68.   // Allocate memory for matrix data
  69.   SetLength(M, nr, nc);
  70.   // Read row by row
  71.   for r := 0 to nr-1 do
  72.   begin
  73.     BlockRead(F, M[r, 0], nc*SizeOf(Double), n);
  74.     if n <> nc*SizeOf(Double) then
  75.       WriteLn('Reading dynamic matrix: Mismatch in bytes read.');
  76.   end;
  77.   CloseFile(F);
  78. end;
  79.  
  80. procedure LoadMatrix2x3FromFile(var M: TMatrix_2x3; AFileName: String);
  81. var
  82.   F: File;
  83. begin
  84.   AssignFile(F, AFileName);
  85.   Reset(F);
  86.   BlockRead(F, M[0, 0], SizeOf(TMatrix_2x3));
  87.   CloseFile(F);
  88. end;
  89.  
  90. var
  91.   M: TMatrix;
  92.   M23: TMatrix_2x3;
  93.   i, j: Integer;
  94. begin
  95.   // Begin with static array
  96.   WriteLn('*** STATIC ARRAY ***');
  97.   M23[0, 0] := 0.5;
  98.   M23[0, 1] := 2.4;
  99.   M23[0, 2] := 1.2;
  100.  
  101.   M23[1, 0] := 3.4;
  102.   M23[1, 1] := 5.3;
  103.   M23[1, 2] := 6.5;
  104.  
  105.   for i := 0 to 1 do
  106.   begin
  107.     for j := 0 to 2 do
  108.       Write(M23[i, j]:5:1);
  109.     WriteLn;
  110.   end;
  111.   WriteLn;
  112.  
  113.   SaveMatrix2x3ToFile(M23, 'test23.mat');
  114.   FillChar(M23, Sizeof(M23), 0);
  115.   LoadMatrix2x3FromFile(M23, 'test23.mat');
  116.  
  117.   for i := 0 to 1 do
  118.   begin
  119.     for j := 0 to 2 do
  120.       Write(M23[i, j]:5:1);
  121.     WriteLn;
  122.   end;
  123.   WriteLn;
  124.  
  125.   // Now repeat with dynamic array
  126.   WriteLn('*** DYNAMIC ARRAY ***');
  127.   SetLength(M, 2, 3);
  128.  
  129.   M[0, 0] := 0.5;
  130.   M[0, 1] := 2.4;
  131.   M[0, 2] := 1.2;
  132.  
  133.   M[1, 0] := 3.4;
  134.   M[1, 1] := 5.3;
  135.   M[1, 2] := 6.5;
  136.  
  137.   for i := 0 to 1 do
  138.   begin
  139.     for j := 0 to 2 do
  140.       Write(M[i, j]:5:1);
  141.     WriteLn;
  142.   end;
  143.   WriteLn;
  144.  
  145.   SaveMatrixToFile(M, 'test.mat');
  146.   for i := 0 to 1 do
  147.     for j := 0 to 2 do
  148.       M[i, j] := 0;
  149.   LoadMatrixFromFile(M, 'test.mat');
  150.  
  151.   for i := 0 to 1 do
  152.   begin
  153.     for j := 0 to 2 do
  154.       Write(M[i, j]:5:1);
  155.     WriteLn;
  156.   end;
  157.   WriteLn;
  158.  
  159.   WriteLn('Press ENTER to quit...');
  160.   Readln;
  161. end.
Title: Re: Save and write whole arrays of numbers to the text files
Post by: BobDog on September 27, 2021, 12:30:07 am

Thank you for your excellent treatise wp.
Of course a matrix is always 1,1 based, to use 0,0 base in a dynamic array here would confuse me, so the 0 element is not used, saved and loaded from file yes, but ignored-AND REMEMBERED.
I have set a static matrix 1,1 based, I have also overloaded all these C procedures for dynamic and static matrices.
Code: Pascal  [Select][+][-]
  1. program matrixtofile;
  2.  
  3.     function fopen (p1:pchar; p2:pchar):pointer ; cdecl external 'msvcrt.dll' name 'fopen';
  4.     function fread (p1:pointer;i1:integer;i2:integer;_FILE: pointer):integer ; cdecl external 'msvcrt.dll' name 'fread';
  5.     function fwrite (p1:pointer;i1:integer;i2:integer;_FILE: pointer):integer ; cdecl external 'msvcrt.dll' name 'fwrite';
  6.     function fclose (_FILE:pointer):integer   cdecl external 'msvcrt.dll' name 'fclose';
  7.  
  8.  
  9.   type matrixd=array of array of real;
  10.   type matrix= packed array[1..2, 1..3] of real;
  11.  
  12.  
  13.        function DeleteFile(filename: string): Boolean;
  14.          var
  15.          F: file;
  16.          begin
  17.          Assign(F, filename);
  18.         {$I-}
  19.         Erase(F);
  20.        {$I+}
  21.        DeleteFile:=IOResult=0;
  22.        end;
  23.  
  24.         procedure savefile(content:matrix;filename:pchar);
  25.        var
  26.        w:pchar='wb';
  27.        fp:pointer;
  28.        begin
  29.        fp:=fopen(filename,w);
  30.        if fp = nil then
  31.        begin
  32.        writeln( 'Unable to save  ',filename);
  33.        exit
  34.        end;
  35.         fwrite((@content[1,1]), 1, sizeof(content), fp);
  36.         fclose(fp);
  37.        end;
  38.  
  39.         procedure savefile(content:matrixD;filename:pchar);
  40.        var
  41.        w:pchar='wb';
  42.        fp:pointer;
  43.        begin
  44.        fp:=fopen(filename,w);
  45.        if fp = nil then
  46.        begin
  47.        writeln( 'Unable to save  ',filename);
  48.        exit
  49.        end;
  50.        fwrite(pointer(content), 1, sizeof(content)*length(content), fp);
  51.         fclose(fp);
  52.        end;
  53.  
  54.        procedure loadfile(var content:matrix;filename:pchar);
  55.        var
  56.        w:pchar='rb';
  57.        fp:pointer;
  58.        begin
  59.        fp:=fopen(filename,w);
  60.        if fp = nil then
  61.        begin
  62.        writeln( 'Unable to open  ',filename);
  63.        exit
  64.        end;
  65.        fread(@content[1,1], 1, sizeof(content), fp);
  66.         fclose(fp);
  67.        end;
  68.  
  69.        procedure loadfile(var content:matrixD;filename:pchar);
  70.        var
  71.        w:pchar='rb';
  72.        fp:pointer;
  73.        begin
  74.        fp:=fopen(filename,w);
  75.        if fp = nil then
  76.        begin
  77.        writeln( 'Unable to open  ',filename);
  78.        exit
  79.        end;
  80.        fread(pointer(content), 1, sizeof(content)*length(content), fp);
  81.         fclose(fp);
  82.        end;
  83.  
  84.  
  85.    var
  86.    i,j:int32;
  87.    filename:pchar='array.dat';
  88.     m: matrix;
  89.     ret:matrix;
  90.     m2:matrixD;
  91.     ret2:matrixD;
  92.  
  93.  
  94.    begin
  95.       //================ static ======================
  96.      m[1,1]:=0.5;
  97.      m[1,2]:=2.4 ;
  98.      m[1,3]:=1.2;
  99.  
  100.      m[2,1]:=3.4;
  101.      m[2,2]:=5.3 ;
  102.      m[2,3]:=6.5;
  103.  
  104.     writeln('To file - staic');
  105.     for i:=1 to 2 do
  106.       for j:=1 to 3 do
  107.       begin
  108.       write(m[i,j],',');
  109.       if (j=3) then writeln;
  110.       end;
  111.  
  112.     savefile(m,filename);
  113.  
  114.       FillChar(ret, Sizeof(ret), 0);
  115.       loadfile(ret,filename);
  116.         writeln;
  117.        writeln('from file - static');
  118.       for i:=1 to 2 do
  119.       for j:=1 to 3 do
  120.       begin
  121.       write(ret[i,j],',');
  122.       if (j=3) then writeln;
  123.       end;
  124.       writeln;
  125.  
  126.       //================ dynamic ======================
  127.        SetLength(m2,3,4);
  128.      m2[1,1]:=-0.5;
  129.      m2[1,2]:=-2.4 ;
  130.      m2[1,3]:=-1.2;
  131.  
  132.      m2[2,1]:=-3.4;
  133.      m2[2,2]:=-5.3 ;
  134.      m2[2,3]:=-6.5;
  135.  
  136.      writeln('To file - dynamic');
  137.     for i:=1 to 2 do
  138.       for j:=1 to 3 do
  139.       begin
  140.       write(m2[i,j],',');
  141.       if (j=3) then writeln;
  142.       end;
  143.       savefile(m2,filename);
  144.  
  145.       setlength(ret2,3,4);
  146.       loadfile(ret2,filename);
  147.         writeln;
  148.        writeln('from file - dynamic');
  149.       for i:=1 to 2 do
  150.       for j:=1 to 3 do
  151.       begin
  152.       write(ret2[i,j],',');
  153.       if (j=3) then writeln;
  154.       end;
  155.       writeln;
  156.       writeln(deletefile(filename));
  157.    readln;
  158.  
  159.    end.
  160.  
It seems to work, but I note that some members dislike using 'msvcrt.dll', and I don't know if 'c' works in Linux.
By the way, you say that :
"And Lazarus is not forgiving any mistake here"
I haven't tested using the Lazarus ide, I use a simple ide for pascal.
fpc 3.2.2, 64 bits, win 10.
Thank you again.




Title: Re: Save and write whole arrays of numbers to the text files
Post by: jamie on September 27, 2021, 01:18:52 am
In Pascal arrays can start at any value, not just 0 but that is only for the compiler, they all start at the same place.

so if you want to index via 1 instead of 0 for the start, then you specify that. the starting place for 1 is the same address as a starting place of 0.

 when you save and load these arrays, they will be placed at the same position, the start, no matter the way they were originally saved.

 Pascal allows you to specify an array to start at any value as a i said, that does mean they are actually starting up stream somewhere, they are not. Its for the compiler to know what it needs to offset the values for to obtain the 0 offset from values your code indexes the address with.

Title: Re: Save and write whole arrays of numbers to the text files
Post by: wp on September 27, 2021, 10:44:44 am
jamie, no debate on static arrays, but dynamic array always start with index 0.

BogDob, of course you can try dynamic arrays with one extra element to begin with index 1 for your use. But I doubt that you (or worse, every other programmer seeing your code) will remember that always. Your code will be full of unlogical things like SetLength(matrix, n+1, m+1) where n und m are the matrix dimensions (1-based), or n := Length(matrix)-1, or first_row_index := Low(matrix)+1. All other dynamic arrays in your code, as well as list elements (TStringList, TList, ...) are 0-based and miss that additional 1. That's highly error-prone.

The only situation in which I myself would accept such an "extended" pseudo-1-based dynamic array is inside a class/object which hides all these internal details from the user, e.g:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. type
  4.   TMatrix = object
  5.   private
  6.     FElements: array of array of double;
  7.     function GetElement(i, j: Integer): Double;  // i/j 1-based
  8.     procedure GetSize(out n, m: Integer);
  9.     procedure SetElement(i, j: Integer; AValue: Double);
  10.   public
  11.     procedure Init(n, m: Integer);
  12.     // procedure Done;  // not needed because FElements is destroyed automatically.
  13.     property Elements[i, j: Integer]: Double read GetElement write SetElement; default;
  14.   end;
  15.  
  16.   procedure TMatrix.Init(n, m: Integer);
  17.   begin
  18.     SetLength(FElements, n+1, m+1);
  19.   end;
  20.  
  21.   function TMatrix.GetElement(i, j: Integer): Double;
  22.   begin
  23.     Result := FElements[i-1, j-1];
  24.   end;
  25.  
  26.   procedure TMatrix.GetSize(out n, m: Integer);
  27.   begin
  28.     n := Length(FElements)-1;
  29.     m := Length(FElements[0])-1;
  30.   end;
  31.  
  32.   procedure TMatrix.SetElement(i, j: Integer; AValue: Double);
  33.   begin
  34.     FElements[i-1, j-1] := AValue;
  35.   end;
  36.  
  37. var
  38.   Mat: TMatrix;
  39.   i, j: Integer;
  40.   n, m: Integer;
  41. begin
  42.   Mat.Init(2, 2);
  43.   Mat[1, 1] := 11;
  44.   Mat[1, 2] := 12;
  45.   Mat[2, 1] := 21;
  46.   Mat[2, 2] := 22;
  47.   Mat.GetSize(n, m);
  48.   for i := 1 to n do
  49.   begin
  50.     for j := 1 to m do
  51.       Write(Mat[i, j]:5:0);
  52.     WriteLn;
  53.   end;
  54.  
  55.   ReadLn;
  56. end.  /code]
  57.  
Title: Re: Save and write whole arrays of numbers to the text files
Post by: BobDog on September 27, 2021, 11:32:49 am

Hi wp
I think you just have to get into a matrix mode for the (type matrix) and throw extreme caution to the wind.
I have tried a matrix object in other languages, it is all very well, but I always have preferred straight arrays for matrix operations, they are faster and cleaner IMHO.
My previous matrix mode in this forum:
https://forum.lazarus.freepascal.org/index.php/topic,55254.msg410793.html#msg410793
And of course the dynamic array approach is better I think.
Just look upon the operation as using a subset of the pointer.
But then again nobody responded to my matrix in that post, so you might have the better idea after all.




Title: Re: Save and write whole arrays of numbers to the text files
Post by: Thaddy on September 27, 2021, 11:40:35 am
You really do not have to use msvcrt since Pascal has all the equivalents for the filehandling you do here.
See the examples in the manual.
Here is one of them:
Code: Pascal  [Select][+][-]
  1. Program Example6;
  2.  
  3. { Program to demonstrate the BlockRead and BlockWrite functions. }
  4.  
  5. Var Fin, fout : File;
  6.     NumRead,NumWritten : Word;
  7.     Buf : Array[1..2048] of byte;
  8.     Total : Longint;
  9.  
  10. begin
  11.   Assign (Fin, Paramstr(1));
  12.   Assign (Fout,Paramstr(2));
  13.   Reset (Fin,1);
  14.   Rewrite (Fout,1);
  15.   Total:=0;
  16.   Repeat
  17.     BlockRead (Fin,buf,Sizeof(buf),NumRead);
  18.     BlockWrite (Fout,Buf,NumRead,NumWritten);
  19.     inc(Total,NumWritten);
  20.   Until (NumRead=0) or (NumWritten<>NumRead);
  21.   Write ('Copied ',Total,' bytes from file ',paramstr(1));
  22.   Writeln (' to file ',paramstr(2));
  23.   close(fin);
  24.   close(fout);
  25. end.
The link is here and subsequent links can be found there too:
https://www.freepascal.org/docs-html/rtl/system/blockread.html
This is also fully cross-platform and only uses the system unit.

But as suggested,  TFilestream is a bit more convenient.
TinyPortal © 2005-2018