Reputation: 23
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
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
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