Recent

Author Topic: Save and write whole arrays of numbers to the text files  (Read 11581 times)

matandked

  • Guest
Save and write whole arrays of numbers to the text files
« 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.  

Thaddy

  • Hero Member
  • *****
  • Posts: 14169
  • Probably until I exterminate Putin.
Re: Save and write whole arrays of numbers to the text files
« Reply #1 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?
Specialize a type, not a var.

lainz

  • Hero Member
  • *****
  • Posts: 4449
    • https://lainz.github.io/
Re: Save and write whole arrays of numbers to the text files
« Reply #2 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.
« Last Edit: February 03, 2017, 06:23:46 pm by lainz »

Handoko

  • Hero Member
  • *****
  • Posts: 5122
  • My goal: build my own game engine using Lazarus
Re: Save and write whole arrays of numbers to the text files
« Reply #3 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. ...
...
« Last Edit: February 03, 2017, 06:33:08 pm by Handoko »

matandked

  • Guest
Re: Save and write whole arrays of numbers to the text files
« Reply #4 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:
  • will read data from a set of Excel workbooks or text files >:D
  • perform calculations (currently I have no details, but there will be some simple arithmetic operations as well as linear programming tasks) on just read arrays of numbers 
  • save output to Excel spreadsheet and text file


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-)

User137

  • Hero Member
  • *****
  • Posts: 1791
    • Nxpascal home
Re: Save and write whole arrays of numbers to the text files
« Reply #5 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.

maurobio

  • Hero Member
  • *****
  • Posts: 623
  • Ecology is everything.
    • GitHub
Re: Save and write whole arrays of numbers to the text files
« Reply #6 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,
UCSD Pascal / Burroughs 6700 / Master Control Program
Delphi 7.0 Personal Edition
Lazarus 2.0.12 - FPC 3.2.0 on GNU/Linux Mint 19.1, Lubuntu 18.04, Windows XP SP3, Windows 7 Professional, Windows 10 Home

Bart

  • Hero Member
  • *****
  • Posts: 5265
    • Bart en Mariska's Webstek
Re: Save and write whole arrays of numbers to the text files
« Reply #7 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).

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

Thaddy

  • Hero Member
  • *****
  • Posts: 14169
  • Probably until I exterminate Putin.
Re: Save and write whole arrays of numbers to the text files
« Reply #8 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.
« Last Edit: September 26, 2021, 12:53:13 pm by Thaddy »
Specialize a type, not a var.

BobDog

  • Sr. Member
  • ****
  • Posts: 394
Re: Save and write whole arrays of numbers to the text files
« Reply #9 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.  

Bart

  • Hero Member
  • *****
  • Posts: 5265
    • Bart en Mariska's Webstek
Re: Save and write whole arrays of numbers to the text files
« Reply #10 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

BobDog

  • Sr. Member
  • ****
  • Posts: 394
Re: Save and write whole arrays of numbers to the text files
« Reply #11 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).



wp

  • Hero Member
  • *****
  • Posts: 11832
Re: Save and write whole arrays of numbers to the text files
« Reply #12 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.

BobDog

  • Sr. Member
  • ****
  • Posts: 394
Re: Save and write whole arrays of numbers to the text files
« Reply #13 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.





wp

  • Hero Member
  • *****
  • Posts: 11832
Re: Save and write whole arrays of numbers to the text files
« Reply #14 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.  

 

TinyPortal © 2005-2018