Nat Berman
Nat Berman

Reputation: 23

Trouble with FileIO in Pascal

I'm having trouble creating a working save system for a final project in my Introduction to programming class. I am trying to save Records of dogs in an array. Each dog has a name, owner name, breed, gender(1 and 2 relates back to yes and no), spayed / neutered status (again 1 and 2 relates to yes and no), as well as an array of weights and BCS scores stored within the record that are all drawn in separate indexes of the overall dog array. The system I have setup imports 1 dog perfectly but as soon as I add another dog to be brought into the terminal I get errors about access violations. I've hand executed many times and can't seem to find the issue. I'll attach the code below. Thanks!!!

type
DogInformation = record
    OwnerName: String;
    name: String;
    breed: String;
    gender: Integer;
    weight: array of Integer;
    spay_neut: Integer;
    bcs: array of Integer;

    end;

DogArray = array of DogInformation;

procedure SaveAllData(var dog: DogArray);
var
i, o, z: Integer;
StoredData: Text;
len: Integer;
begin   
    FileCreate('/Users/Nat/Desktop/data.txt');
    AssignFile(StoredData, '/Users/Nat/Desktop/data.txt');
    Reset(StoredData);
    Rewrite(StoredData);    
                for i := 0 to (High(dog) - 1) do
                    begin
                        WriteLn(StoredData, '!');
                        WriteLn(StoredData, dog[i].name);
                        WriteLn(StoredData, '@');
                        WriteLn(StoredData, dog[i].OwnerName);
                        WriteLn(StoredData, '#');
                        WriteLn(StoredData, dog[i].breed);
                        WriteLn(StoredData, '$');
                        WriteLn(StoredData, dog[i].gender);
                        WriteLn(StoredData, '%');
                        WriteLn(StoredData, dog[i].spay_neut);
                        for o := 0 to High(dog[i].weight) do
                            begin
                                WriteLn(StoredData, '^');
                                WriteLn(StoredData, dog[i].weight[o]);      
                            end;
                        for z := 0 to High(dog[i].bcs) do
                            begin
                                WriteLn(StoredData, '&');
                                WriteLn(StoredData, dog[i].bcs[z]);
                            end;
                        WriteLn(StoredData, '?');
                    end;
    len := (Length(dog) - 1);
    WriteLn(StoredData, '!');
    WriteLn(StoredData, dog[len].name);
    WriteLn(StoredData, '@');
    WriteLn(StoredData, dog[len].OwnerName);
    WriteLn(StoredData, '#');
    WriteLn(StoredData, dog[len].breed);
    WriteLn(StoredData, '$');
    WriteLn(StoredData, dog[len].gender);
    WriteLn(StoredData, '%');
    WriteLn(StoredData, dog[len].spay_neut);
    for o := 0 to High(dog[len].weight) do
        begin
            WriteLn(StoredData, '^');
            WriteLn(StoredData, dog[len].weight[o]);        
        end;
    for z := 0 to High(dog[len].bcs) do
        begin
            WriteLn(StoredData, '&');
            WriteLn(StoredData, dog[len].bcs[z]);
        end;
    WriteLn(StoredData, '~');

    Close(StoredData);
end;


procedure LoadAllData(var dog: DogArray);
var
ident: String;
i, o, z: Integer;
StoredData: Text;
skip: String;
begin
    AssignFile(StoredData, '/Users/Nat/Desktop/data.txt');
    Reset(StoredData);
    i := 0;
    o := 0;
    z := 0;

    SetLength(dog,1);
    SetLength(dog[i].weight,0);
    SetLength(dog[i].bcs,0);

    repeat
        begin
            ReadLn(StoredData, ident);          
            if ident = '!' then
                begin
                    WriteLn('Importing Dogs Name.......');
                    ReadLn(StoredData, dog[i].name);
                    WriteLn(dog[i].name);
                end;    

            if ident = '?' then
                begin
                    SetLength(dog, (Length(dog))+1);
                    i := i + 1;
                end;

            if ident = '@' then
                begin
                    WriteLn('Importing Owner Name......');
                    ReadLn(StoredData, dog[i].OwnerName);
                    WriteLn(dog[i].OwnerName);
                end;

            if ident = '#' then
                begin
                    WriteLn('Importing Breed...........');
                    ReadLn(StoredData, dog[i].breed);
                    WriteLn(dog[i].breed);
                end;

            if ident = '$' then
                begin
                    WriteLn('Importing Gender..........');
                    ReadLn(StoredData, dog[i].gender);
                    WriteLn(dog[i].gender);
                end;

            if ident = '%' then
                begin
                    WriteLn('Importing Spay/Neut.......');
                    ReadLn(StoredData, dog[i].spay_neut);
                    WriteLn(dog[i].spay_neut);
                end;

            if ident = '^' then
                begin
                    WriteLn('Importing Weights.........');
                    SetLength(dog[i].weight, (Length(dog[i].weight))+1);
                    ReadLn(StoredData, (dog[i].weight[o]));
                    WriteLn(dog[i].weight[o], ' ');
                    o := o + 1;
                end;

            if ident = '&' then
                begin
                    WriteLn('Importing BCS.............');
                    SetLength(dog[i].bcs, (Length(dog[i].bcs))+1);
                    ReadLn(StoredData, (dog[i].bcs[z]));
                    WriteLn(dog[i].bcs[z], ' ');
                    z := z + 1;
                end;
        end;
    until ident = '~';
    WriteLn('');
    WriteLn('Import Complete...........');
    Close(StoredData);
end;

Any information or advice will help!

Thanks, Nat.

Edit - This is what the output in terminal looks like for the second dog. Everything comes out except the dogs Weight and BCS, then when I try and edit the dog array by adding a new dog for example, the program quits citing an access violation.

Data.txt Output Terminal Output

Upvotes: 1

Views: 67

Answers (2)

asd-tm
asd-tm

Reputation: 5263

I decided to write another answer as well. I have given an answer that stated an exact solution of your problem. But I feel I need to explain that the aproach you were using was non-optimal from the very begining... It was not the way Pascal dialects were to be used.

Let us start.

First. Pascal developers commonly use T before the names of user-defined types: I renamed the record declaration. Here is the declaration.

type
  TDogInformation = record
    OwnerName: String;
    // other fields...
  end;

The next step. I declared the following variables:

var
  fl: file;
  RecSize: word;      // This variable will be used for fast data access
  DI: array of TDogInformation;
  i: word;

I populated the data in this way you may use any other.

  Setlength(DI, 2);
  with DI[0] do
  begin
    OwnerName := 'First owner';
    name := 'First dog';
    breed := 'Mixed';
    gender := 0;
    Setlength(weight, 2);
    weight[0] := 56;
    weight[1] := 60;
    spay_neut := 1;
    Setlength(bcs, 1);
    bcs[0] := 15;
  end;
  with DI[1] do
  begin
    OwnerName := 'Second owner';
    name := 'Second dog';
    breed := 'Mixed mixed';
    gender := 1;
    Setlength(weight, 1);
    weight[0] := 64;
    spay_neut := 1;
    Setlength(bcs, 12);
    bcs[0] := 16;
    bcs[1] := 18;
  end;

Now we make fast writing of the data. Only few lines of code are required for the whole procedure.

  AssignFile(fl, 'b:\asd.txt');
  ReWrite(fl);
  for i := 0 to High(DI) do
  begin
    RecSize := SizeOf(DI[i]);   //<--We calculate and keep the buffer size 
    BlockWrite(fl, RecSize, 2); //<--and write it in the file to simlify reading
    BlockWrite(fl, DI[i], RecSize);//<--Now we write the record at once.
  end;
  CloseFile(fl);

And here will be the fast reading.

  AssignFile(fl, 'b:\asd.txt');
  SetLength(DI, 0);//line is compulsory. We reinitialize DI
  Reset(fl);
  i := 0;
  repeat
    Setlength(DI, i+1);
    BlockRead(fl, RecSize, SizeOf(RecSize) { =2 as it is a word });
    //^ You will get the record size by reading the word variable first
    BlockRead(fl, DI[i], RecSize); //Now read the record using its fetched size
    WriteLn(DI[i].OwnerName);//output the records' data
    Inc(i);
  until Eof(fl);
  CloseFile(fl);
  ReadLn;

That's all. You do need need to parse the text data. And finally. There are techniques that allow to write whole collections at once (for example, using TStream/TMemoryStream/TFileStream) without any parsing and/or setting/fetching individual records' lengths.

Upvotes: 0

asd-tm
asd-tm

Reputation: 5263

You need to attentively reinitialize the variables in your loading procedure at every iteration of the loop:

  i := 0;
  repeat
    o := 0;
    z := 0;
    SetLength(dog, i+1);
    SetLength(dog[i].weight, 0);
    SetLength(dog[i].bcs, 0);

    ...

    inc(i);
  until...

Otherwise o and z variable are incremented to the values higher than the upper borders of weight and bcz arrays.

Upvotes: 1

Related Questions