Pascal runtime error (range overrun) while reading array from file - arrays

I am trying to write a program to read a long list of book(1000 books),isbn etc
but when the program runs, it shows range overrun
the format of txt is
1
1234567890
ABC book
peter
20
2
1234567896
...
the code is:
const maxbk=1000;
type bookrecord = record
book_no:string;
isbn:string;
book_name:string;
author:string;
borrowed:string;
end;
var booklist : array[1..maxbk] of bookrecord;totalbook:integer;
procedure readbooklist(var bklist:array of bookrecord;var totalbk:integer);
var f:text;temp:string;code:integer;
begin
totalbk:=0;
assign(f,'bklist.txt');
reset(f);
while not eof(f) do
begin
readln(f,bklist[totalbk+1].book_no);
readln(f,bklist[totalbk+1].isbn);
readln(f,bklist[totalbk+1].book_name);
readln(f,bklist[totalbk+1].author);
readln(f,bklist[totalbk+1].borrowed);
totalbk:=totalbk+1;
end;
close(f);
writeln('read file done');
end;
begin
readbooklist(booklist,totalbook);
end.
who can help to fix the problem??

I think the problem is in your handling of the array parameter. Try this (highlighted in bold are the changes I've added):
const maxbk=1000;
type bookrecord = record
book_no:string;
isbn:string;
book_name:string;
author:string;
borrowed:string;
end;
var booklist : array[1..maxbk] of bookrecord; totalbook:integer;
procedure readbooklist(var bklist:array of bookrecord;var totalbk:integer);
var f:text;temp:string;code:integer;
begin
totalbk:=Low(bklist);
assign(f,'bklist.txt');
reset(f);
while not eof(f) do
begin
readln(f,bklist[totalbk].book_no);
readln(f,bklist[totalbk].isbn);
readln(f,bklist[totalbk].book_name);
readln(f,bklist[totalbk].author);
readln(f,bklist[totalbk].borrowed);
totalbk:=totalbk+1;
end;
totalbk := totalbk - Low(bklist);
close(f);
writeln('read file done');
end;
begin
readbooklist(booklist,totalbook);
end.
Also, a few choice spaces would help with readability (like a space after each comma and around assignment operators).
Note, too, that your code (and the changed code I'm providing) don't check for incomplete records in your input text file or properly check for blank lines, etc (e.g., invalid book_no values). You should attempt to add some code which makes it a little more resilient to problems in the input file. As others have pointed out, there are probably better ways to structure the input and read it as well.

Related

Retrieving data with single quote from dataset (Delphi)

Currently I have something like but I'm unable to retrieve my data from the dataset when it has single quote in the data
procedure TForm1.AfterConstruction;
begin
inherited;
cdsMain.FieldDefs.Add('ItemCode', ftWideString, 20);
cdsMain.CreateDataSet;
cdsDetail.FieldDefs.Add('ItemCode', ftWideString, 20);
cdsDetail.FieldDefs.Add('Project', ftWideString, 20);
cdsDetail.CreateDataSet;
var S := '6x8''''';
cdsMain.AppendRecord([S]);
cdsDetail.AppendRecord([S, 'P01']);
cdsDetail.AppendRecord([S, 'P02']);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if FDConnection1.Connected then
FDConnection1.Close;
if FDLocalSQL1.Active then
FDLocalSQL1.Active := False;
FDLocalSQL1.Active := True;
FDQuery1.Open('SELECT A.ItemCode, B.Project FROM Main A INNER JOIN Detail B ON (A.ItemCode=B.ItemCode)');
end;
My expected result are
ItemCode Project
6x8' P01
6x8' P02
The error I get when I use var S := '6x8''''
Source code
From what I can deduce from the source files, there's a bug in FireDAC in this case. If I eliminate the ON clause in the SQL, it works, even with an ASCII Single-Quote (#39). If I replace the ASCII Single-Quote (#39) with the UNICODE Single-Quote (#8216) or remove it altogether, it works.
It seems like the parser for TFDLocalSQL (I think) has a bug when it tries to resolve the ON clause and there's an ASCII Single-Quote involved.
You should report the error to Embarcadero at https://quality.embarcadero.com, including your source code and an explanation.

TStringList in an array in Lazarus

I have a problem with TStringLists in Lazarus. I have an array called 'trans' of records called 'TTrans' which contain, among other things, TStringList called 'meebetalers'. So when I need to know for example the amount of lines in that StringList I would have to write this right?
trans[i].meebetalers.Count;
Anyways, I first create a stringlist and put the selected strings from a checklistbox in it, and that works (i.e. the program returns 3 when I ask for the Count, which is correct).
In this piece of code I add values to the StringList:
slmeebetalers := TStringList.Create;
for i:= 0 to Form6.CLBox.Count-1 do begin
if Form6.CLBox.Checked[i] then begin
slmeebetalers.Add(Form6.CLBox.Items[i]);
end;
end;
Then I put the stringlist a procedure, and in that procedure I assign my first created StringList to the stringlist I mentionned before (trans[i].meebetalers), see my piece of code next.
Unit6.VoegTransToe(Form6.TransNaam.Text,
Form6.TrComboBox.Text,
bedrag,
slmeebetalers,
Form6.CalendarDialog1.Date);
But when I then ask for the count, it returns 0.
procedure VoegTransToe(naam, betaalpers: string; bedrag: currency;
meebetalers: TStringList; datum: TDateTime);
begin
aantaltrans:= aantaltrans+1;
trans[aantaltrans].naam:=naam;
trans[aantaltrans].pers.naam:=betaalpers;
trans[aantaltrans].bedrag:=bedrag;
trans[aantaltrans].datum:=datum;
meebetalers:= TStringList.Create;
trans[aantaltrans].meebetalers:= TStringList.Create;
trans[aantaltrans].meebetalers.Assign(meebetalers);
meebetalers.Free;
//trans[aantaltrans].meebetalers.Free;
end;
note The difference in name of the variable is because they are in different units
With this code I don't get an error, but it returns 0. When I say //meebetalers.Free; the same happens.
But when I add //trans[aantaltrans].meebetalers.Free; I don't get an error while compiling, but when I call the procedure. Then I get this error:
Project project1 raised exception class 'External: SIGSEGV'.
I think there is something wrong with the Create and Free function, but I don't know what. When I implement the try...finally...end it returns the same error. Can anybody help me?
The problem is that your VoegTransToe() procedure is ignoring the populated TStringList object that is passed in via its meebetalers parameter. You are resetting meebetalers to point at a newly created empty TStringList object just before assigning meebetalers to trans[aantaltrans].meebetalers.
procedure VoegTransToe(naam, betaalpers: string; bedrag: currency;
meebetalers: TStringList; datum: TDateTime);
begin
aantaltrans:= aantaltrans+1;
trans[aantaltrans].naam:=naam;
trans[aantaltrans].pers.naam:=betaalpers;
trans[aantaltrans].bedrag:=bedrag;
trans[aantaltrans].datum:=datum;
// meebetalers:= TStringList.Create; // <-- GET RID OF THIS!
trans[aantaltrans].meebetalers:= TStringList.Create;
trans[aantaltrans].meebetalers.Assign(meebetalers);
//meebetalers.Free; // <-- AND THIS!
end;
Don't forget to Free() the input TStringList object when you are done using it:
slmeebetalers := TStringList.Create;
try
for i := 0 to Form6.CLBox.Count-1 do begin
if Form6.CLBox.Checked[i] then begin
slmeebetalers.Add(Form6.CLBox.Items[i]);
end;
end;
Unit6.VoegTransToe(..., slmeebetalers, ...);
finally
slmeebetalers.Free;
end;

How do i call a function within a function?

i'm creating a mini program in pascal to read music albums. The problem i'm facing is calling the readTracks() function within my readAlbum() function. Any help would be great, thank you :)
The current error when executing the code is:
Error: Incompatible types: got "albumRecord" expected "albumRecord.Dynamic Array Of trackRecord
type
trackRecord = record
trackName:string;
fileLocation: string;
end;
albumRecord = record
albumName:string;
tracks: array of trackRecord;
end;
function readTracks():albumRecord;
var
i:Integer;
numOfTracks:Integer;
begin
numOfTracks := readInteger('Enter number of tracks: ');
setLength(result.tracks, numOfTracks);
for i:= 0 to high(result.tracks)do
begin
Writeln('Enter Track ',i+1,' Details: ');
result.tracks[i].trackName := readString('Enter track name: ');
result.tracks[i].fileLocation := readString('Enter file
location: ');
end;
end;
function readAlbum (prompt: string): albumRecord;
begin
result.albumName := readString('Enter Album Name: ');
result.tracks := readTracks();
end;
I think you have managed to confuse yourself by the way you have declared your albumRecord.Tracks. What you should have done is to declare a trackArray type and declared your readTracks to return an instance of this array type.
Your main problem was that your readAlbum returns an albumRecord, but you had set up its tracks field to be assigned from readTracks, which returns the wrong type, i.e. albumRecord, rather than an array of tracks.
Put another way, as you have declared readAlbum to return an albumRecord, the compiler will generate code which, on entry to the function, sets up an instance of albumRecord on the stack, which is eventually returned as the return value of the function when it completes. Your code in the function is simply to fill out the fields of this record, not create another instance of it as your result.tracks := readTracks() would have done, given how you had declared readTracks.
The code below changes the declarations and code inside readTracks so that it does what you intend.
uses TerminalUserInput;
type
trackRecord = record
trackName:string;
fileLocation: string;
end;
trackArray = array of trackRecord;
albumRecord = record
albumName:string;
tracks: trackArray;
end;
function readTracks():trackArray;
var
i:Integer;
numOfTracks:Integer;
begin
numOfTracks := readInteger('Enter number of tracks: ');
setLength(result, numOfTracks);
for i:= 0 to high(result)do
begin
Writeln('Enter Track ',i+1,' Details: ');
result[i].trackName := readString('Enter track name: ');
result[i].fileLocation := readString('Enter file location: ');
end;
end;
function readAlbum (prompt: string): albumRecord;
begin
result.albumName := readString('Enter Album Name: ');
result.tracks := readTracks();
end;
begin
readAlbum('New album');
end.
Btw, I think you would find your code much clearer, when you come back to it after a while if you got into the habit of using a naming convention for declarations of record- and array types which distinguishes them from instances of them. One convention is to precede the type-name with a 'T', so your would be TtrackRecord, TalbumRecord, TtrackArray.
Also btw, in your q you say
The current error when executing the code is: Error: Incompatible types
Actually, that is not quite correct. The error occurs while the compiler is compiling the code, not when your code is executing. This is an important difference: your error is known as a compile-time error, whereas one which occurs when your program is executing (which it can only do once it has been successfully compiled) is known as a run-time error.
tracks is an array, and thus needs indexing. Which number are you reading?
In the for loop you do know how to index the array, so why can't you do it in readalbum?

Reading and processing file in Pascal won't work after first line

I'm having trouble reading more than one line from a data file in Pascal. It gives me an "Invalid numeric format" run-time error when I try to read in more than one line (I tested just the first line separately and it works fine). This makes me think that it has something to do with the carriage return at the end of a line.
Here is the code that should read in all of the lines from my .DAT file:
program commission;
var
moreRec:Boolean;
FileOut:Text;
FileIn:Text;
DRONE_ID:String[9];
DRONE_NAME:String[18];
SALES:Real;
COMM:Real;
procedure header;
begin
writeln(FileOut, Space(16),'SALES COMMISSION REPORT');
writeln(FileOut);
writeln(FileOut,' SSN',Space(10),'SALESPERSON',Space(9),'SALES COMMISSION');
writeln(FileOut);
end;
procedure readRec;
begin
if EOF(FileIn) THEN
moreRec:=false
else
read(FileIn, DRONE_ID);
read(FileIn, DRONE_NAME);
read(FileIn, SALES);
COMM:=SALES*0.03;
end; {readRec}
procedure initial;
begin
moreRec:=true;
Assign(FileIn, 'PRG2-150.DAT ');
Reset(FileIn);
Assign(FileOut,'output.txt');
Rewrite(FileOut);
readRec
end; {initial}
procedure process;
begin
write(FileOut, DRONE_ID);
write(FileOut, Space(2));
write(FileOut, DRONE_NAME);
write(FileOut, Space(5));
write(FileOut, SALES:9:2);
write(FileOut, Space(3));
writeln(FileOut, COMM:8:2);
readRec
end; {process}
procedure wrapup;
begin
Close(FileOut);
Close(FileIn);
end; {wrapup}
begin
initial;
header;
while moreRec = true do
process;
wrapup;
end.
And here is the .DAT file that I am reading from:
998874673Joe Smith 27.65
849773298Sue Williams 35.90
445861253Al Oop 54.90
584988754Diane Mindykowski 25.96
758423652Alicen Morse 53.35
485236845Burton Schuring 58.52
586974512Linda Gillam 69.35
I'm new to Pascal but I'd love to learn why my program won't read in more than one line.
Thanks
I think you'll need a
readln(FileIn);
towards the end of readRec to skip past the CR/LF delimiter to the next line.
Pascal wants the data fields in text files to be white-space delimited. The problem is that you have no space between the Drone_Id and Drone_Name fields.
998874673Joe Smith 27.65
_________^__ Insert space here.
You also should use a readln for the last field on the line (Sales).
EDIT: Sorry the space isn't needed there (I was thinking that the first field was numeric). But do make sure you use readln on the last field of the line.

pascal illegal qualifier error when calling function from a procedure

function classes(o:integer): String;
var allclasses : array[1..7] of String;
begin
allclasses[1]:= 'class1';
allclasses[2]:= 'class2';
allclasses[3]:= 'class3';
allclasses[4]:= 'class4';
allclasses[5]:= 'class5';
allclasses[6]:= 'class6';
allclasses[7]:= 'class7';
classes := allclasses[o];
end;
Above you can see a function, which should receive an integer and give a result of string that was stored in array.
procedure loadthis(chosen : string);
var f: text;
i : integer;
begin
Assign(f, 'files\'+chosen+'.txt');
Reset(f);
ReadLn(f, i);
MyChar.clas := classes[i];
end;
When this procedure is called, it calls a "classes" function. Pleae note that Mychar ir a global variable.
begin
loadthis(FileName);
ReadLn;
Readln
end.
Ant this is the main program, which calls "loadthis" procedure.
I Have no idea whats wrong, but I am getting these errors:
Wrong amount of parameters specified
Illegal qualifier
Both errors come from this line:
MyChar.clas := classes[i];. I have really no idea what is wrong, maybe I can not call a function from a procedure ? Please help.
You're trying to access it as an array index, but it needs to be a function call:
MyChar.clas := classes(i); { note () instead of [] }
You should probably add some range checking, too. What happens if someone puts 20 in the text file? Your array only has items at indexes 1 through 7, so you'll get a runtime error when you call classes(20) with the out of range value.
(You could probably use a constant array for allclasses to lessen your code as well, but your instructor probably haven't gotten that far yet.)
Given your comment about not having an instructor, here's a suggestion about a better way to handle the function:
function classes(o:integer): String;
const
allclasses: array[1..7] of string = ('class1',
'class2',
'class3',
'class4',
'class5',
'class6',
'class7');
begin
{
Low() returns the lowest index of the array, and
High() returns the highest. The if statement makes sure
that o is between them. It is the range check I mentioned.
}
if (o >= Low(allclasses)) and (o <= High(allclasses)) then
classes := allclasses[o]
else
classes := '';
end;

Resources