Recent

Author Topic: Pascal - Arrays of undefined length and files  (Read 9881 times)

Tom_1279

  • New member
  • *
  • Posts: 9
Pascal - Arrays of undefined length and files
« on: November 28, 2014, 01:11:22 am »
Hi all,

I have been learning Pascal for the last couple of months as it's the language we're being taught in 6th form.

I have been writing a system for a library that will allow creation/editing/deletion of books/clients/staff, it will also allow books to be lent out, etc.

However I have recently encountered an issue and am struggling to see any way around it.

Within my program I have a record called TClient which stores several variables, one of these is an array of book references (two integers that point me to a certain book), this allows me to see which books a client is currently borrowing:

  TClient = Record
    ID: String[5];
    Name: String[50];
    Surname: String[50];
    Address: TAddress;                               //contains full address information- not really important here
    BooksOut: Array[0..0] of TBookRef;
  End;

However, when writing to a File you must define variable size (so String[5] rather than String) otherwise the program won't run. The issue I am having is that I do not know the size of the BooksOut Array (as people will constantly take out and return books) so ideally I need to edit this during run time.

One way around this would be to define the Array with a set number of indices that will more than cover the requirements of the program, for example, 50 indices:

BooksOut: Array[0..49] of TBookRef;

However, this could cause issues if I do not set enough indices aside. I also feel that this is bad programming practice- setting aside so much memory (as I will have an Array of TClients, all with BooksOut, and this isn't the only circumstance in which I will need to do this).

Surely there is a way around this in pascal, however if not does anybody have suggestions about how else I could go around the issue (it seems ridiculous to have a seperate file with hundreds of reference numbers which will then have to be associated with clients in a new way- most likely requiring further variables).

If not I may just have to rewrite all of my records and go about the system in a completely new way.

Thank you in advance,

Tom
« Last Edit: November 28, 2014, 02:16:31 am by Tom_1279 »

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Pascal - Arrays of undefined length and files
« Reply #1 on: November 28, 2014, 01:38:27 am »
Everything really depends on how you are managing the persistence of those records and which compiler you are going to use. In FPC you can use dynamic arrays something along the lines of
Code: [Select]
  TClient = Record
    ID: String[5];
    Name: String[50];
    Surname: String[50];
    Address: TAddress;
    BooksOut: Array of TBookRef;
  End;

procedure AddNewBook(var aClient:TClient; const aBookRef:TBookRef);
begin
  Setlength(aClient.BooksOut, Length(aClient.BooksOut)+1);
  aClient.BooksOut[High(aClient.BooksOut)] := aBookRef;
end;

Procedure DeleteBookRef(var aClient:TClient; const aBookRef:TBookRef);
  function BookRefCompare(const BookRef1, BookRef2:TBookRef):Integer; 
  begin
     if bookref1.ID = BookRef2.ID then result :=0 else
     If BookRef1.ID >BookRef2.ID then REsult :=1 else
     Result := -1;
  end;
  procedure MoveOnePosLeft(const StartPos:integer);
  var
    vCntr : integer;
  begin
    For vCntr := StartPos+1 to High(aClient.BookRef) do begin
      aClient.BooksOut[vCntr-1] := aClient.BooksOut[vCntr]);
    end;
  end;

var
  Cntr:integer;
begin
  For Cntr := Low(aClient.BooksOut) to High(aClient.BooksOut) do begin
    if BookRefCompare(aClient.BooksOut[Cntr], aBookRef) =0 then begin
      MoveOnePosLeft(Cntr+1);
      Break;
    end;
  end;
end;


Well you get the idea I hope, if not then fill free to ask more specific questions.
« Last Edit: November 28, 2014, 01:41:30 am by taazz »
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

Cyrax

  • Hero Member
  • *****
  • Posts: 836
Re: Pascal - Arrays of undefined length and files
« Reply #2 on: November 28, 2014, 01:44:21 am »
How about using classes and class persistence? There you don't need to specify sizes for Strings. Another way is ORM and database files (tiOPF etc).

Tom_1279

  • New member
  • *
  • Posts: 9
Re: Pascal - Arrays of undefined length and files
« Reply #3 on: November 28, 2014, 01:51:00 am »
Everything really depends on how you are managing the persistence of those records and which compiler you are going to use. In FPC you can use dynamic arrays something along the lines of
Code: [Select]
  TClient = Record
    ID: String[5];
    Name: String[50];
    Surname: String[50];
    Address: TAddress;
    BooksOut: Array of TBookRef;
  End;

procedure AddNewBook(var aClient:TClient; const aBookRef:TBookRef);
begin
  Setlength(aClient.BooksOut, Length(aClient.BooksOut)+1);
  aClient.BooksOut[High(aClient.BooksOut)] := aBookRef;
end;

Procedure DeleteBookRef(var aClient:TClient; const aBookRef:TBookRef);
  function BookRefCompare(const BookRef1, BookRef2:TBookRef):Integer; 
  begin
     if bookref1.ID = BookRef2.ID then result :=0 else
     If BookRef1.ID >BookRef2.ID then REsult :=1 else
     Result := -1;
  end;
  procedure MoveOnePosLeft(const StartPos:integer);
  var
    vCntr : integer;
  begin
    For vCntr := StartPos+1 to High(aClient.BookRef) do begin
      aClient.BooksOut[vCntr-1] := aClient.BooksOut[vCntr]);
    end;
  end;

var
  Cntr:integer;
begin
  For Cntr := Low(aClient.BooksOut) to High(aClient.BooksOut) do begin
    if BookRefCompare(aClient.BooksOut[Cntr], aBookRef) =0 then begin
      MoveOnePosLeft(Cntr+1);
      Break;
    end;
  end;
end;


Well you get the idea I hope, if not then fill free to ask more specific questions.

Hi,

First, thanks for taking the time to help.

I have been able to use a dynamic array and setlength to edit the array within my TBookRef, however the issue I have had is then writing this to a file (as length must be predetermined). The way I have gone about it at the moment is to have an array of clients:

MyClient: Array of TClient;
ClientFile: File of TClient;

To then use the information from the file I have been doing the following:

Number of clients is stored in MyClient[0].ID...

Procedure UpdateMyClient;
Var
  Counter: Int64;
Begin
  Counter:= 1;
  Seek(ClientFile, 0);
  Read(ClientFile, MyClient[0]);
  SetLength(MyClient, StrToInt64(MyClient[0].ID));
    Try
      While Not EOF(ClientFile) Do Begin
        Seek(ClientFile, Counter);
        Read(ClientFile, MyClient[Counter]);
        Counter:= Counter + 1;
      End;
    Except
      write('EXCEPTION RAISED');
    End;
    HighestClientRecord:= High(MyClient);
End;

This leaves me with an array of clients which can be edited before being stored back in the file. However I have not yet had to use the BooksOut Array- as you saw in my previous post it only has one index (0) for now, so I need to work out how I will get around this. Storage in a .dat file however is vital to the system


Thanks again,

Tom
« Last Edit: November 28, 2014, 01:56:05 am by Tom_1279 »

Tom_1279

  • New member
  • *
  • Posts: 9
Re: Pascal - Arrays of undefined length and files
« Reply #4 on: November 28, 2014, 01:54:27 am »
How about using classes and class persistence? There you don't need to specify sizes for Strings. Another way is ORM and database files (tiOPF etc).

Hi,

I haven't really had much experience with classes/class persistence in Pascal. I briefly used classes when learning C++, however it was soon after this that I moved into Pascal (because of school). Nor have I used ORM and database files (well I've used .dat)- I recall seeing mention of other database files before, however I haven't seen anything in the textbooks I've been learning from- I'll have a look around online and see what I can do.


Thanks,

Tom
« Last Edit: November 28, 2014, 02:33:54 am by Tom_1279 »

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Pascal - Arrays of undefined length and files
« Reply #5 on: November 28, 2014, 04:25:24 am »
Everything really depends on how you are managing the persistence of those records and which compiler you are going to use. In FPC you can use dynamic arrays something along the lines of
Code: [Select]
  TClient = Record
    ID: String[5];
    Name: String[50];
    Surname: String[50];
    Address: TAddress;
    BooksOut: Array of TBookRef;
  End;

procedure AddNewBook(var aClient:TClient; const aBookRef:TBookRef);
begin
  Setlength(aClient.BooksOut, Length(aClient.BooksOut)+1);
  aClient.BooksOut[High(aClient.BooksOut)] := aBookRef;
end;

Procedure DeleteBookRef(var aClient:TClient; const aBookRef:TBookRef);
  function BookRefCompare(const BookRef1, BookRef2:TBookRef):Integer; 
  begin
     if bookref1.ID = BookRef2.ID then result :=0 else
     If BookRef1.ID >BookRef2.ID then REsult :=1 else
     Result := -1;
  end;
  procedure MoveOnePosLeft(const StartPos:integer);
  var
    vCntr : integer;
  begin
    For vCntr := StartPos+1 to High(aClient.BookRef) do begin
      aClient.BooksOut[vCntr-1] := aClient.BooksOut[vCntr]);
    end;
  end;

var
  Cntr:integer;
begin
  For Cntr := Low(aClient.BooksOut) to High(aClient.BooksOut) do begin
    if BookRefCompare(aClient.BooksOut[Cntr], aBookRef) =0 then begin
      MoveOnePosLeft(Cntr+1);
      Break;
    end;
  end;
end;


Well you get the idea I hope, if not then fill free to ask more specific questions.

Hi,

First, thanks for taking the time to help.

I have been able to use a dynamic array and setlength to edit the array within my TBookRef, however the issue I have had is then writing this to a file (as length must be predetermined). The way I have gone about it at the moment is to have an array of clients:

MyClient: Array of TClient;
ClientFile: File of TClient;

To then use the information from the file I have been doing the following:

Number of clients is stored in MyClient[0].ID...

Procedure UpdateMyClient;
Var
  Counter: Int64;
Begin
  Counter:= 1;
  Seek(ClientFile, 0);
  Read(ClientFile, MyClient[0]);
  SetLength(MyClient, StrToInt64(MyClient[0].ID));
    Try
      While Not EOF(ClientFile) Do Begin
        Seek(ClientFile, Counter);
        Read(ClientFile, MyClient[Counter]);
        Counter:= Counter + 1;
      End;
    Except
      write('EXCEPTION RAISED');
    End;
    HighestClientRecord:= High(MyClient);
End;

This leaves me with an array of clients which can be edited before being stored back in the file. However I have not yet had to use the BooksOut Array- as you saw in my previous post it only has one index (0) for now, so I need to work out how I will get around this. Storage in a .dat file however is vital to the system


Thanks again,

Tom

In most RDBMS what you describe is called a master detail relationship and they save it in different tables because 1) you can have as many rows as required by each client and not a prespecified number of fields fro all of them and 2) it is far easier to have a second container shaping it to save what ever data required in what ever size/number is required than having everything in a single continues space that needs to be modified with every change is made to the record (a book was returned today and  two new were taken out after a few days).

So write a smart record that will hold the extra info required when saving and the book info when in memory and save those data in different files for now you can go one for each client (my preference) or one for all the clients.

Its 5 am here and I'm in no position to write anything meaningful at this time so I'll take a small rain check for later today after I had a few hours of sleep. I will write a small example to get you started.


Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

Tom_1279

  • New member
  • *
  • Posts: 9
Re: Pascal - Arrays of undefined length and files
« Reply #6 on: November 28, 2014, 12:12:19 pm »
In most RDBMS what you describe is called a master detail relationship and they save it in different tables because 1) you can have as many rows as required by each client and not a prespecified number of fields fro all of them and 2) it is far easier to have a second container shaping it to save what ever data required in what ever size/number is required than having everything in a single continues space that needs to be modified with every change is made to the record (a book was returned today and  two new were taken out after a few days).

So write a smart record that will hold the extra info required when saving and the book info when in memory and save those data in different files for now you can go one for each client (my preference) or one for all the clients.

Its 5 am here and I'm in no position to write anything meaningful at this time so I'll take a small rain check for later today after I had a few hours of sleep. I will write a small example to get you started.
[/quote]

Hi,

Thanks again.

I understand what you're saying in regards to using tables to store clients which would be an ideal situation, however I've had no experience with this kind of file handling (currently I have just been using .dat files but this obviously throws up the issue with array sizes). It would be much appreciated if you could write out a bit of code if you get chance so that I can see properly how the system, and the code could work.

Regards,

Tom

User137

  • Hero Member
  • *****
  • Posts: 1791
    • Nxpascal home
Re: Pascal - Arrays of undefined length and files
« Reply #7 on: November 28, 2014, 02:39:57 pm »
Just so you know, read() method already moves the read position in file, you don't have to seek() it. Also you cannot read or write a whole dynamic array, as you are trying to do with BooksOut: Array of TBookRef; , it will simply give it an invalid address for value and no books read.

Also if first index is 0 as you said, then
SetLength(MyClient, StrToInt64(MyClient[0].ID));
with 1 client in file would setlength(MyClient, 0). Then you would need
StrToInt64(MyClient[0].ID)+1

Why is it a string[5] anyway? You can represent values from 0..65535 with type "word", which uses just 2 bytes.
« Last Edit: November 28, 2014, 02:44:39 pm by User137 »

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11383
  • FPC developer.
Re: Pascal - Arrays of undefined length and files
« Reply #8 on: November 28, 2014, 02:55:59 pm »
After opening of the file, but before reading the records, you can obtain the number of records with filesize(). You can use this for your setlength, followed by a for loop of read commands.

Tom_1279

  • New member
  • *
  • Posts: 9
Re: Pascal - Arrays of undefined length and files
« Reply #9 on: November 28, 2014, 03:06:55 pm »
Just so you know, read() method already moves the read position in file, you don't have to seek() it. Also you cannot read or write a whole dynamic array, as you are trying to do with BooksOut: Array of TBookRef; , it will simply give it an invalid address for value and no books read.

Also if first index is 0 as you said, then
SetLength(MyClient, StrToInt64(MyClient[0].ID));
with 1 client in file would setlength(MyClient, 0). Then you would need
StrToInt64(MyClient[0].ID)+1

Why is it a string[5] anyway? You can represent values from 0..65535 with type "word", which uses just 2 bytes.

Hi,

Thanks for the advice, the issues you've pointed out are mainly there because the system used to work in a slightly different way, for example I wasnt seeking consecutive locations within the file.

However the setlength isn't setting the array length to 0 - I have already ensured this won't happen during file initialisation.

I will however change the string to a word which I hadn't previously considered.

Finally, I am aware that I cannot write the dynamic array to a file and this the problem I need advice regarding (I am struggling to come up with a way around this that won't involve allocating lots of memory pointlessly)


Thanks for taking the time to help,

Tom

Tom_1279

  • New member
  • *
  • Posts: 9
Re: Pascal - Arrays of undefined length and files
« Reply #10 on: November 28, 2014, 03:10:07 pm »
After opening of the file, but before reading the records, you can obtain the number of records with filesize(). You can use this for your setlength, followed by a for loop of read commands.

Ah great idea, I didn't even think to do that for some some reason.

Thanks,

Tom

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Pascal - Arrays of undefined length and files
« Reply #11 on: November 29, 2014, 03:05:33 am »
Here this is a small unit that has procedures to initialize, dispose, save and load clients and bookrefs keep in mind that I have compiled once to make sure that there are no type errors but that is as far as my tests wend so although you have  the the borders explained there you need to test it and weed out any details that need weeding.
Code: [Select]
unit uClients;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils;
type

  TBookRef = record
    BookID       :Int64;
    CheckOutDate :TDateTime;
  end;
  TAddress = record
    StreetName :string[100];
    StreetNo   :string[10];
    Zip        :string[10];
    City,
    Country    :string[80];
  end;

  TBookRefArray = array of TBookRef;
  PBookRefArray = ^TBookRefArray;
  TBookOut = record
    Case integer of
      1 : (BooksOut:PBookRefArray);
      2 : (Count:PtrInt);
  end;

  TClient = Record
    ID       :String[5];
    Name     :String[50];
    Surname  :String[50];
    Address  :TAddress;
    case byte of
      1 :(BookCount:Int64; StartIndex:Int64);
      2 :(BooksOut:PBookRefArray);
  End;
  TClientArray = array of TClient;
  TClientFile  = file of TClient;
  TBookRefFile = file of TBookRef;

procedure InitAddress(var aAddress:TAddress);
procedure InitClient(var aClient:TClient);
function NewClient:TClient;
procedure AddNewBook(var aClient:TClient; const aBookRef:TBookRef);
procedure DeleteBookRef(var aClient:TClient; const aBookRef:TBookRef);
procedure DisposeClient(var aClient:TClient);
function BookRefCompare(const BookRef1, BookRef2:TBookRef):Integer;
function SaveClient(const aClient:TClient; var aFile:TClientFile; var aRefFile:TBookRefFile):Integer;
function SaveClients(const aClients:TClientArray; var aFile:TClientFile; var aRefFile:TBookRefFile):Integer;
function SaveBookRefs(const aBookRefArray :TBookRefArray; var aFile:TBookRefFile):Integer;
function LoadBookRefs(const aBookRefArray :TBookRefArray; var aFile:TBookreFile; const aRefCount:Int64):Integer;
function LoadClient(var aClient :TClient; var aClientFile:TClientFile; var aRefFile:TBookRefFile):Integer;
function LoadClients(var aClients :TClientArray; var aClientFile:TClientFile; var aRefFile:TBookRefFile):Integer;

implementation

function BookRefCompare(const BookRef1, BookRef2:TBookRef):Integer;
begin
   if BookRef1.BookID = BookRef2.BookID then Result := 0 else
   If BookRef1.BookID > BookRef2.BookID then Result := 1 else
   Result := -1;
end;


procedure InitAddress(var aAddress:TAddress);
begin
  aAddress.City       :='';
  aAddress.Country    :='';
  aAddress.StreetName :='';
  aAddress.Zip        :='';
  aAddress.StreetNo   :='';
end;

procedure InitClient(var aClient:TClient);
begin
  aClient.ID      :='';
  aClient.Name    :='';
  aClient.Surname :='';
  InitAddress(aClient.Address);
  aClient.BooksOut:=nil;
end;

procedure DisposeClient(var aClient:TClient);
begin
  SetLength(aClient.BooksOut^, 0);
  Dispose(aClient.BooksOut);
  InitClient(aClient);
end;

Function NewClient:TClient;
begin
  InitClient(Result);
  New(Result.BooksOut);
  SetLength(Result.BooksOut^,0);
end;

procedure AddNewBook(var aClient:TClient; const aBookRef:TBookRef);
begin
  Setlength(aClient.BooksOut^, Length(aClient.BooksOut^)+1);
  aClient.BooksOut^[High(aClient.BooksOut^)] := aBookRef;
end;

Procedure DeleteBookRef(var aClient:TClient; const aBookRef:TBookRef);
  procedure MoveOnePosLeft(const StartPos:integer);
  var
    vCntr : integer;
  begin
    For vCntr := StartPos+1 to High(aClient.BooksOut^) do begin
      aClient.BooksOut^[vCntr-1] := aClient.BooksOut^[vCntr];
    end;
  end;

var
  Cntr:integer;
begin
  For Cntr := Low(aClient.BooksOut^) to High(aClient.BooksOut^) do begin
    if BookRefCompare(aClient.BooksOut^[Cntr], aBookRef) = 0 then begin
      MoveOnePosLeft(Cntr+1);
      Break;
    end;
  end;
end;

function SaveClient(const aClient:TClient; var aFile:TClientFile; var aRefFile:TBookRefFile):Integer;
var
  vPos, vCount : Int64;
  vClient      : TClient;
begin
  try
    vClient := aClient;
    vClient.StartIndex := FilePos(aRefFile);
    vClient.BookCount  := Length(aClient.BooksOut^);
    SaveBookRefs(aClient.BooksOut^, aRefFile);
    Write(aFile, aClient);
  except
    on E:EDivByZero do exit(-2);
    on E:ERangeError do exit(-3);
    on E:Exception do exit(-1); //This one catches all exceptions known exceptions must be handled before this line. //unknown error.
  end;
  Result := 0;//no error
end;

function SaveClients(const aClients :TClientArray; var aFile :TClientFile; var aRefFile :TBookRefFile) :Integer;
var
  vCntr :Integer;
begin
  Result := 0; //assume everything is OK.
  for vCntr := Low(aClients) to High(aClients) do begin
    Result := SaveClient(aClients[vCntr], aFile, aRefFile);
    if Result <> 0 then Break;
  end;
end;

function SaveBookRefs(const aBookRefArray :TBookRefArray; var aFile :TBookRefFile) :Integer;
var
  vCntr:Integer;
begin
  try

    for vCntr := low(aBookRefArray) to High(aBookRefArray) do
      Write(aFile, aBookRefArray[vCntr]);

  except
    on E:EDivByZero do exit(-2);
    on E:ERangeError do exit(-3);
    on E:Exception do exit(-1); //This one catches all exceptions known exceptions must be handled before this line. //unknown error.
  end;
  Result := 0;//all's well
end;

function LoadBookRefs(const aBookRefArray :TBookRefArray; var aFile:TBookreFile; const aRefCount:Int64):Integer;
var
  vCntr : Integer =0;
begin
  Result := 0;
  try
    SetLength(aBookRefArray,RefCount);
    repeat
      Read(aFile, aBookRefArray[vCntr]);
      inc(vCntr);
    until vCntr >= RefCount;
  except
    on E:EDivByZero  do exit(-2);
    on E:ERangeError do exit(-3);
    on E:Exception   do exit(-1); //This one catches all exceptions known exceptions must be handled before this line. //unknown error.
  end;
end;

function LoadClient(var aClient :TClient; var aClientFile:TClientFile; var aRefFile:TBookRefFile):Integer;
var
  vClient : TClient;
begin
  Result := 0;
  try
    Read(aClientFile,vClient);
    FileSeek(aRefFile, vClient.StartIndex);
    LoadBookRefs(aClient.BooksOut^, aRefFile, vClient.BookCount);
    aClient.ID       := vClient.ID;
    aClient.Name     := vClient.Name;
    aClient.Surname  := vClient.Surname;
    aClient.Address  := vClient.Address;
  except
    on E:EDivByZero  do exit(-2);
    on E:ERangeError do exit(-3);
    on E:Exception   do exit(-1); //This one catches all exceptions known exceptions must be handled before this line. //unknown error.
  end;
end;

function LoadClients(var aClients :TClientArray; var aClientFile:TClientFile; var aRefFile:TBookRefFile):Integer;
begin
  repeat
    SetLength(aClients,Length(aClients)+1);
    Result := LoadClient(aClients[high(aClients)], aClientFile, aRefFile);
    if Result <> 0 then Exit;
  until EOF(aClientFile);
end;

end.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

Tom_1279

  • New member
  • *
  • Posts: 9
Re: Pascal - Arrays of undefined length and files
« Reply #12 on: November 29, 2014, 03:52:28 am »
Here this is a small unit that has procedures to initialize, dispose, save and load clients and bookrefs keep in mind that I have compiled once to make sure that there are no type errors but that is as far as my tests wend so although you have  the the borders explained there you need to test it and weed out any details that need weeding.
Code: [Select]
unit uClients;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils;
type

  TBookRef = record
    BookID       :Int64;
    CheckOutDate :TDateTime;
  end;
  TAddress = record
    StreetName :string[100];
    StreetNo   :string[10];
    Zip        :string[10];
    City,
    Country    :string[80];
  end;

  TBookRefArray = array of TBookRef;
  PBookRefArray = ^TBookRefArray;
  TBookOut = record
    Case integer of
      1 : (BooksOut:PBookRefArray);
      2 : (Count:PtrInt);
  end;

  TClient = Record
    ID       :String[5];
    Name     :String[50];
    Surname  :String[50];
    Address  :TAddress;
    case byte of
      1 :(BookCount:Int64; StartIndex:Int64);
      2 :(BooksOut:PBookRefArray);
  End;
  TClientArray = array of TClient;
  TClientFile  = file of TClient;
  TBookRefFile = file of TBookRef;

procedure InitAddress(var aAddress:TAddress);
procedure InitClient(var aClient:TClient);
function NewClient:TClient;
procedure AddNewBook(var aClient:TClient; const aBookRef:TBookRef);
procedure DeleteBookRef(var aClient:TClient; const aBookRef:TBookRef);
procedure DisposeClient(var aClient:TClient);
function BookRefCompare(const BookRef1, BookRef2:TBookRef):Integer;
function SaveClient(const aClient:TClient; var aFile:TClientFile; var aRefFile:TBookRefFile):Integer;
function SaveClients(const aClients:TClientArray; var aFile:TClientFile; var aRefFile:TBookRefFile):Integer;
function SaveBookRefs(const aBookRefArray :TBookRefArray; var aFile:TBookRefFile):Integer;
function LoadBookRefs(const aBookRefArray :TBookRefArray; var aFile:TBookreFile; const aRefCount:Int64):Integer;
function LoadClient(var aClient :TClient; var aClientFile:TClientFile; var aRefFile:TBookRefFile):Integer;
function LoadClients(var aClients :TClientArray; var aClientFile:TClientFile; var aRefFile:TBookRefFile):Integer;

implementation

function BookRefCompare(const BookRef1, BookRef2:TBookRef):Integer;
begin
   if BookRef1.BookID = BookRef2.BookID then Result := 0 else
   If BookRef1.BookID > BookRef2.BookID then Result := 1 else
   Result := -1;
end;


procedure InitAddress(var aAddress:TAddress);
begin
  aAddress.City       :='';
  aAddress.Country    :='';
  aAddress.StreetName :='';
  aAddress.Zip        :='';
  aAddress.StreetNo   :='';
end;

procedure InitClient(var aClient:TClient);
begin
  aClient.ID      :='';
  aClient.Name    :='';
  aClient.Surname :='';
  InitAddress(aClient.Address);
  aClient.BooksOut:=nil;
end;

procedure DisposeClient(var aClient:TClient);
begin
  SetLength(aClient.BooksOut^, 0);
  Dispose(aClient.BooksOut);
  InitClient(aClient);
end;

Function NewClient:TClient;
begin
  InitClient(Result);
  New(Result.BooksOut);
  SetLength(Result.BooksOut^,0);
end;

procedure AddNewBook(var aClient:TClient; const aBookRef:TBookRef);
begin
  Setlength(aClient.BooksOut^, Length(aClient.BooksOut^)+1);
  aClient.BooksOut^[High(aClient.BooksOut^)] := aBookRef;
end;

Procedure DeleteBookRef(var aClient:TClient; const aBookRef:TBookRef);
  procedure MoveOnePosLeft(const StartPos:integer);
  var
    vCntr : integer;
  begin
    For vCntr := StartPos+1 to High(aClient.BooksOut^) do begin
      aClient.BooksOut^[vCntr-1] := aClient.BooksOut^[vCntr];
    end;
  end;

var
  Cntr:integer;
begin
  For Cntr := Low(aClient.BooksOut^) to High(aClient.BooksOut^) do begin
    if BookRefCompare(aClient.BooksOut^[Cntr], aBookRef) = 0 then begin
      MoveOnePosLeft(Cntr+1);
      Break;
    end;
  end;
end;

function SaveClient(const aClient:TClient; var aFile:TClientFile; var aRefFile:TBookRefFile):Integer;
var
  vPos, vCount : Int64;
  vClient      : TClient;
begin
  try
    vClient := aClient;
    vClient.StartIndex := FilePos(aRefFile);
    vClient.BookCount  := Length(aClient.BooksOut^);
    SaveBookRefs(aClient.BooksOut^, aRefFile);
    Write(aFile, aClient);
  except
    on E:EDivByZero do exit(-2);
    on E:ERangeError do exit(-3);
    on E:Exception do exit(-1); //This one catches all exceptions known exceptions must be handled before this line. //unknown error.
  end;
  Result := 0;//no error
end;

function SaveClients(const aClients :TClientArray; var aFile :TClientFile; var aRefFile :TBookRefFile) :Integer;
var
  vCntr :Integer;
begin
  Result := 0; //assume everything is OK.
  for vCntr := Low(aClients) to High(aClients) do begin
    Result := SaveClient(aClients[vCntr], aFile, aRefFile);
    if Result <> 0 then Break;
  end;
end;

function SaveBookRefs(const aBookRefArray :TBookRefArray; var aFile :TBookRefFile) :Integer;
var
  vCntr:Integer;
begin
  try

    for vCntr := low(aBookRefArray) to High(aBookRefArray) do
      Write(aFile, aBookRefArray[vCntr]);

  except
    on E:EDivByZero do exit(-2);
    on E:ERangeError do exit(-3);
    on E:Exception do exit(-1); //This one catches all exceptions known exceptions must be handled before this line. //unknown error.
  end;
  Result := 0;//all's well
end;

function LoadBookRefs(const aBookRefArray :TBookRefArray; var aFile:TBookreFile; const aRefCount:Int64):Integer;
var
  vCntr : Integer =0;
begin
  Result := 0;
  try
    SetLength(aBookRefArray,RefCount);
    repeat
      Read(aFile, aBookRefArray[vCntr]);
      inc(vCntr);
    until vCntr >= RefCount;
  except
    on E:EDivByZero  do exit(-2);
    on E:ERangeError do exit(-3);
    on E:Exception   do exit(-1); //This one catches all exceptions known exceptions must be handled before this line. //unknown error.
  end;
end;

function LoadClient(var aClient :TClient; var aClientFile:TClientFile; var aRefFile:TBookRefFile):Integer;
var
  vClient : TClient;
begin
  Result := 0;
  try
    Read(aClientFile,vClient);
    FileSeek(aRefFile, vClient.StartIndex);
    LoadBookRefs(aClient.BooksOut^, aRefFile, vClient.BookCount);
    aClient.ID       := vClient.ID;
    aClient.Name     := vClient.Name;
    aClient.Surname  := vClient.Surname;
    aClient.Address  := vClient.Address;
  except
    on E:EDivByZero  do exit(-2);
    on E:ERangeError do exit(-3);
    on E:Exception   do exit(-1); //This one catches all exceptions known exceptions must be handled before this line. //unknown error.
  end;
end;

function LoadClients(var aClients :TClientArray; var aClientFile:TClientFile; var aRefFile:TBookRefFile):Integer;
begin
  repeat
    SetLength(aClients,Length(aClients)+1);
    Result := LoadClient(aClients[high(aClients)], aClientFile, aRefFile);
    if Result <> 0 then Exit;
  until EOF(aClientFile);
end;

end.

Hi,

Thank you so much for this, I'll need to have another read over your code in more detail after I've had some sleep and then I'll look at trying to implement something similar into my current code- I want to take plenty of time to fully interpret and understand what you've done however.

I've had a pretty good look through just and it definitely seems to solve my query but as I say I'll be back in touch once I've had a bit of a play around tomorrow. 

Thanks again,

Tom

v.denis

  • Guest
Re: Pascal - Arrays of undefined length and files
« Reply #13 on: November 29, 2014, 10:24:29 am »
IMO such system should be based on some database, like sqlite or other, rather than writing own from scratch.
« Last Edit: November 29, 2014, 10:26:29 am by v.denis »

Tom_1279

  • New member
  • *
  • Posts: 9
Re: Pascal - Arrays of undefined length and files
« Reply #14 on: November 29, 2014, 11:11:20 am »
IMO such system should be based on some database, like sqlite or other, rather than writing own from scratch.

Hi,

SQL databases are probably ideal and would have been a great option in the circumstances, however I am wanting to implement the other suggested solution because it will give me more of an insight into how the language works snd will allow me to implement similar solutions in the future if necessary. Once I've done this I'll read up on database implementation using SQLLite. However I am having to move the code between home and school. At home I use an up to date version of lazarus, but the school have a very old version of Borland which I have been having to compensate for as there are several units (such as CRT which I'm hoping to try and add) missing. Once I know that SQL implementation is an option there, I will look into learning how to use it with Pascal.

Thanks,

Tom

 

TinyPortal © 2005-2018