Pascal: Only displaying one set of array's and not the rest from file - pascal

So my code only reads one set of albums from my textfile and refused to read the rest in the file. For some reason why I get it to read album, it only displays that the file contain "1" album instead of "2".Even though in my loop i've SetLength the array to equal the albumNumber so the array size should be set to that when i begin my for loop.
textfile
2 (how many albums)
Adele (name)
Pop (genre)
2 (tracks)
Hello (Track 1)
ff (location)
Remedy (track 2)
dd (location)
Jcole
Rap
2
Toto
ff
Africa
dd
pascal
type
TrackRec = record
name: String;
location: String;
end;
GenreType = (Pop, Rap, Rock, Classic);
AlbumRec = Record
name: String;
genre: GenreType;
tracks: array of TrackRec;
end;
type AlbumArray = array of AlbumRec;
procedure ReadAlbum(var albums: AlbumArray; var myFile: TextFile);
var
albumNumber, tracknumber, count, i: Integer;
begin
AssignFile(myFile, 'mytestfile.dat');
Reset(myFile);
ReadLn(myFile, albumNumber);
WriteLn('This file contained ', albumNumber, ' album/s');
SetLength(albums, albumNumber);
for i := 0 to High(albums) do
begin
ReadLn(myFile, albums[i].name);
ReadLn(myFile, albums[i].genre);
ReadLn(myFile, tracknumber);
SetLength(albums[i].tracks, tracknumber);
for count := Low(albums[count].tracks) to tracknumber - 1 do
begin
ReadLn(myFile, albums[i].tracks[count].name);
ReadLn(myFile, albums[i].tracks[count].location);
end;
end;
end;
procedure Main();
var
i, count, select, change: Integer;
albums: AlbumArray;
myFile: TextFile;
message: String;
begin
WriteLn('Please select an option: ');
WriteLn('-------------------------');
WriteLn('1. Read Filename');
WriteLn('2. Display Albums');
WriteLn('3. Select an Album');
WriteLn('4. Update an Album');
WriteLn('5. Exit');
WriteLn('-------------------------');
repeat
i := ReadInteger('Select option for menu:');
case i of
1: ReadAlbum(albums, myFile);
2: PrintAll(albums, myFile);
3: PlayAlbum(albums, myFile);
4: Update(albums, myFile);
end;
until i = 5;
end;

Your code includes a number of routines (e.g. PlayAlbum) for which you
have not included the source.
Anyway, you may be relieved to know that in fact your ReadAlbum procedure does
work correctly, but perhaps you have gotten into a muddle thinking that it does not.
Down below, I've explained how you could have debugged it
to verify that it works. For now, replace your ReadAlbum and Main by the code below,
which also includes a couple of procedures to display the results.
Once you've done that, compile and run the app.
Code:
procedure ReadAlbum(var albums: AlbumArray; var myFile: TextFile);
var
albumNumber, tracknumber, count, i: Integer;
begin
AssignFile(myFile, 'D:\Delphi\Code\Lazarus\mytestfile.dat');
Reset(myFile);
ReadLn(myFile, albumNumber);
WriteLn('This file contained ', albumNumber, ' album/s');
WriteLn; // To space out the display
SetLength(albums, albumNumber);
for i := 0 to High(albums) do
begin
ReadLn(myFile, albums[i].name);
ReadLn(myFile, albums[i].genre);
ReadLn(myFile, tracknumber);
SetLength(albums[i].tracks, tracknumber);
for count := Low(albums[count].tracks) to tracknumber - 1 do
begin
ReadLn(myFile, albums[i].tracks[count].name);
ReadLn(myFile, albums[i].tracks[count].location);
end;
end;
end;
procedure PrintAlbum(Albums : AlbumArray; AlbumNumber : Integer);
var
Tracks : Integer;
Album : AlbumRec;
begin
// Note : This is incomplete, you should complete it yourself!
// I've used the local variable Album to avoid having to keep typing
// Albums[AlbumNumber] and because it makes inspection during debugging easier
Album := Albums[AlbumNumber];
WriteLn('Album number: ', AlbumNumber);
Writeln('AlbumName: ', Album.Name);
Writeln('AlbumGenre: ', Album.Genre);
Writeln; // to Space out the display;
end;
procedure PrintAlbums(Albums : AlbumArray);
var
AlbumNumber : Integer;
Album : AlbumRec;
begin
for AlbumNumber := 0 to High(Albums) do begin
Album := Albums[AlbumNumber];
PrintAlbum(Albums, AlbumNumber);
end;
end;
procedure Main();
var
albums: AlbumArray;
myFile: TextFile;
begin
ReadAlbum(Albums, MyFile);
PrintAlbums(Albums);
WriteLn('Done');
ReadLn;
end;
begin
Main;
end.
The following explains how to start debugging in the freeware Lazarus
IDE for FPC.
1 With your project open in the IDE, set a debugger breakpoint on the line
for i := 0 to High(albums) do
by either pressing F5 or clicking the blue circle in the "gutter"
on the left of the editor window. Either way, the line should turn red.
2 Compile and run the application. The black console window should appear
and then the debugger will stop on the BP set in step 1.
3 Press Ctrl-F5. This will pop op a Watch Properties dialog.
A watch is a way to get the debugger to display the value of a variable
as the program executes. Enter
Albums
in the Expression box.
If necessary, drag the Watch List window so that it doesn't overlap the code
editor window or the console window.
4 Now, repeatedly press F8 and observe the Watch List window carefully. F8 causes the debugger to execute the program a line at a time, called "single-stepping" for
obvious reasons. Note that the Watch List window already "knows" that there are 2 Album records and what there fields are.
As you single step through the for i:= and for count :=, notice how the fields
of the two records are progressively filled out.
Btw, Count is not a good name for a variable which actually rep
resents the TrackNumber
5 Eventually, the for i:= loop will finish, and at the point you know that the Albums array is correctly set up, including the second Album record.
6 Once you're a bit more familiar with the debugger, you could delete the BP you set in step 1. Instead, place a BP on the final line of ReadAlbums. After compiling and running the application, it will stop on the BP so that you can verify Albums's content without having to single-step each line of the two for loops.
7 Now, complete the coding of PrintAlbum. If necessary you can debug it as per steps 1-6
8 Search for online Lazarus debugging tutorials and read them until it gets boring.
Hopefully, by the time you've done all this, you'll have a better idea what information (including code) readers need to have in order to help. The key to
getting good help is to enable the reader to reproduce the problem, and your q doesn't do that. Readers shouldn't have to guess what missing code is supposed to do. Even if it is obvious, the problem may actually turn out to be somewhere in the code you haven't included, in which case readers can't help anyway.

Related

Using Inno Setup scripting to insert lines into a text file?

Objective is to insert multiple lines (once) at beginning of text document.
But I have been having problems with the approaches I found. I've attempted to adjust them but it incorporates side-effect issues.
Two problems:
Appends end of file instead of inserting into line locations.
In its present design, it appends the file 3 times.
In reference to the other scripts I found both lines incorporating Result := resulted in unknown identifier.
References:
How to use Inno Setup scripting to append to a text file?
https://www.codeproject.com/Questions/477984/createplusandpluswriteplusbatchplusfileplusinplusi
[Code]
procedure CurStepChanged(CurStep: TSetupStep);
var
ErrorCode: Integer;
FileName: string;
lines : TArrayOfString;
begin
fileName := ExpandConstant('{userappdata}\xy');
fileName := AddBackslash(FileName) + 'zlt.net';
SetArrayLength(lines, 6);
lines[0] := 'A';
lines[1] := 'B';
lines[2] := 'C';
lines[3] := 'D';
lines[4] := 'E';
lines[5] := 'F';
SaveStringsToFile(filename,lines,true);
end;
There's no way to insert lines into file. You have to recreate whole file.
CurStepChanged is triggered for every step of the installation. You will want to insert the lines in one of the steps only (likely ssInstall or ssPostInstall)
procedure CurStepChanged(CurStep: TSetupStep);
var
Lines: TStringList;
begin
if CurStep = ssPostInstall then
begin
Lines := TStringList.Create;
try
Lines.LoadFromFile(FileName);
Lines.Insert(0, 'A');
Lines.Insert(1, 'B');
Lines.Insert(2, 'C');
Lines.Insert(3, 'D');
Lines.Insert(4, 'E');
Lines.Insert(5, 'F');
Lines.SaveToFile(FileName);
finally
Lines.Free;
end;
end;
end;
The code loads whole file to memory. That can be a problem if the file is too large. But that should not be a problem for files smaller than tens of MBs. Also repeatedly calling Insert is not efficient. But for small files and few lines, this should not be a problem either.

Pascal Text Menu Music Player

I need help with creating my music player, I'm receiving the same error and can't seem to get past it. Thank you.
I've attached my code below, as well as my errors.
Errors:
Free Pascal Compiler version 2.6.4 [2014/02/26] for i386 Copyright (c)
1993-2014 by Florian Klaempfl and others Target OS: Darwin for i386
Compiling MusicPlayer.pas
MusicPlayer.pas(82,37) Error: Incompatible type for arg no. 1: Got "ShortString", expected "Album"
MusicPlayer.pas(138,31) Error: Incompatible type for arg no. 1: Got
"albumArray", expected "Album"
MusicPlayer.pas(164,44) Error: Incompatible type for arg no. 1: Got "albumArray", expected "Album"
MusicPlayer.pas(174) Fatal: There were 3 errors compiling module,
stopping Fatal: Compilation aborted Error: /usr/local/bin/ppc386
returned an error exitcode (normal if you did not specify a source
file to be compiled)
program MusicPlayer;
uses TerminalUserInput;
type
Track = record
trackName: String;
location: String;
end;
TrackArray = array of Track;
Album = record
albumName: String;
artistName: String;
genre: String;
track: TrackArray;
// key: Integer;
trackNum: Integer;
fileName: String;
end;
albumArray = array of Album;
function GetAlbums(): albumArray;
var
// anAlbum: Album;
//albums: albumArray;
fileName: String;
myFile: TextFile;
numOfAlb: Integer;
trackNum: Integer;
i: Integer;
j: Integer;
begin
fileName := ReadString('Enter filename: ');
AssignFile(myFile, fileName);
// AssignFile(myFile, 'albums.dat');
Reset(myFile);
ReadLn(myFile, numOfAlb);
setLength(result, numOfAlb);
for i:= 0 to High(result) do
begin
ReadLn(myFile, result[i].albumname);
ReadLn(myFile, result[i].artistName);
ReadLn(myFile, result[i].genre);
ReadLn(myFile, trackNum);
setLength(result[i].track, trackNum);
for j:= 0 to trackNum -1 do
begin
ReadLn(myFile, result[i].track[j].trackName);
ReadLn(myFile, result[i].track[j].location);
end;
end;
end;
procedure DisplayAlbum(a: Album);
var
//t: Track;
i: Integer;
begin
WriteLn('Album name is: ', a.albumName);
WriteLn('Album artist name is: ', a.artistName);
WriteLn('Album genre is: ', a.genre);
WriteLn('Number of tracks are: ', a.trackNum);
for i:= 0 to High(a.track) do
begin
WriteLn('Track name is: ', a.track[i].trackName);
WriteLn('Album name is: ', a.track[i].location);
end;
end;
function PrintAllGenres(albums: albumArray): albumArray;
var
i: Integer;
begin
for i := 0 to High(albums) do
begin
DisplayAlbum(albums[i].genre);
end;
end;
procedure SelectAlbum(const albums: albumArray);
var
val: Integer;
i: Integer;
begin
WriteLn('<< Welcome to the Track Player >>');
val := ReadInteger('Enter an Album''s key number: ');
for i := 0 to High(albums) do
begin
WriteLn('Album is now playing.');
end;
if (i > High(albums)) then
begin
WriteLn('Album was not found, now returning to Main Menu ');
end;
end;
function UpdateAlbum(a: Album): Album;
begin
a.albumName := ReadString('Please enter a new name for this album: ');
a.genre := ReadString('Please enter a new genre for this album: ');
end;
// function UpdateAlbums(): albumArray;
// var
// val: Integer;
// i: Integer;
// begin
// WriteLn('<< Album Updater >>');
// val := ReadInteger('Enter an Album''s key number: ');
// if (val = True) then
// WriteLn('Album was found.')
// else
// WriteLn('Album was not found, now returning to Main Menu ');
// end;
procedure DisplayAlbums(albums: albumArray);
var
val: Integer;
begin
repeat
WriteLn('<< Displaying Albums >>');
WriteLn('1. Display all albums');
WriteLn('2. Display genre');
WriteLn('3. Return to main menu');
val := ReadInteger('Enter a number to enter menu: ');
case val of
1: DisplayAlbum(albums);
2: PrintAllGenres(albums);
end;
until val = 3;
end;
procedure Main();
var
albums: albumArray;
val: Integer;
begin
repeat
WriteLn('<< Text Music Player Menu >>');
WriteLn('1. Read in Albums');
WriteLn('2. Display Albums');
WriteLn('3. Select an Album to play');
WriteLn('4. Update an existing Album');
WriteLn('5. Quit');
val := ReadInteger('Enter a number to enter menu: ');
case val of
1: albums := GetAlbums();
2: DisplayAlbums(albums);
3: SelectAlbum(albums);
4: albums := UpdateAlbum(albums);
end;
until val = 5;
end;
begin
Main();
end.
In your code you have written
procedure DisplayAlbum(a: Album);
which means that you need to pass an Album to the procedure, but on line 82 you have written
DisplayAlbum(albums[i].genre);
genre is a field of an Album while you should pass a whole Album
Change line 82 to
DisplayAlbum(albums[i]);
I leave the other errors for you yourself to work out, the errors are very similar, and you should now be able to sort them out.
As I told you yesterday, you may want to (or actually, need to) speak with your tutor to get a better understanding.

Writing datas to memobox from .txt file using Lazarus freepascal?

I've got a project at schools which requires to write datas from a .txt file to a "memobox" in Lazarus freepascal.
There are datas in order like this.
Budapest tomato 23
Dublin tv 45
Rosslare projector 43
etc.
I have to read these datas from a .txt file and then write them into a memobox in Lazarus freepascal.
If I am not mistaken I have already copyed the datas from the .txt file but I have no idea how to write them.
I've already written this code:
type
cityname:integer;
product:string;
quantity:integer;
var
Form1: TForm1;
ceg:array[1..5] of rektip;
db:integer;
implementation
procedure TForm1.Button1Click(Sender: TObject);
var f:textfile; i:integer; a:char;
begin
assignfile(f,'termek.txt');
reset(f);
readln(f,db);
While not eof(f) do
begin
readln(f,ceg[i].varosnev,a,ceg[i].termek,a,ceg[i].darabszam);
end;
db:=1;
closefile(f);
end;
procedure TForm1.Button2Click(Sender: TObject);
var i:integer;
begin
For i:=1 to db do
Memo1.lines.add(ceg[i].varosnev,ceg[i].termek,IntToStr(ceg[i].darabszam));
end;
end;
I would like to know how to fix it.
The code you posted is incomplete, so I assume:
type
rektip = record
varosnev: string;
termek: string;
darabszam: Integer;
end;
There is a lot wrong with your approach:
readln(f,db);
While not eof(f) do
begin
readln(f,ceg[i].varosnev,a,ceg[i].termek,a,ceg[i].darabszam);
end;
db:=1;
closefile(f);
end;
You are not initializing nor updating i, so you are reading all data into the same record, and you don't even know which one (i could be 100 and you'd be writing the data to somewhere unknown -- there is no ceg[100]). That results in undefined behaviour (and that can be a crash too).
Do something like:
var
ceg: array of rektip;
...
begin
AssignFile(f, 'termek.txt');
Reset(f);
Readln(f, db);
if db = 0 then
Exit;
SetLength(ceg, db);
i := 0;
while not eof(f) do
begin
Readln(f, ceg[i].varosnev, ceg[i].termek, ceg[i].darabszam);
Inc(i);
if i > High(ceg) then
Break;
end;
SetLength(ceg, i); // remove any empty slots.
CloseFile(f);
end;
Now you can put them into the TMemo:
for i := Low(ceg) to High(ceg) do
begin
Memo1.Lines.Add(Format('%s %s %d', [ceg[i].varosnev, ceg[i].termek, ceg[i].darabszam]));
end;
Note that the code above, reading from the file, assumes the file looks like:
3
Budapest tomato 23
Dublin tv 45
Rosslare projector 43
i.e. each "record" is on a line of its own and the first line contains the number of records.

InnoSetup making {app} dir for install.log

I need to create an install.log of the selected components in the install destination folder ({app}) but I'm getting in issue when i run that installer that says "File does not exist C:/tmp/exe/install.log" I'm assuming that means it has not created the dir "exe" yet. How can i circumvent this?
procedure CurStepChanged(CurStep: TSetupStep);
var
I: Integer;
LogList: TStringList;
begin
if CurStep = ssInstall then
begin
LogList := TStringList.Create;
try
LogList.Add('Selected components:');
for I := 0 to WizardForm.ComponentsList.Items.Count - 1 do
if WizardForm.ComponentsList.Checked[I] then
LogList.Add('Component: ' + WizardForm.ComponentsList.ItemCaption[I]);
LogList.SaveToFile(ExpandConstant('{app}\install.log'));
finally
LogList.Free;
end;
end;
end;
I suspect you're trying to access the folder too early in the process, before it's actually been created yet.
Try changing to a later step in the process, such as ssPostInstall. At that point, you'll know for certain that the folder has been created. The rest of your code should be able to stay the same.
procedure CurStepChanged(CurStep: TSetupStep);
var
I: Integer;
LogList: TStringList;
begin
if CurStep = ssPostInstall then
begin
LogList := TStringList.Create;
try
LogList.Add('Selected components:');
for I := 0 to WizardForm.ComponentsList.Items.Count - 1 do
if WizardForm.ComponentsList.Checked[I] then
LogList.Add('Component: ' + WizardForm.ComponentsList.ItemCaption[I]);
LogList.SaveToFile(ExpandConstant('{app}\install.log'));
finally
LogList.Free;
end;
end;
end;

About SelectNext procedure in Delphi XE2

I'm having the next problem with SELECTNEXT and FINDNEXTCONTROL procedures in Delphi XE2 (Update 4 under WinXP Pro 32b), these procedures aren't working correctly. When a control gets focus and I try to pass to next control with the code below, it does not simply work:
procedure TformMain.cbServicioKeyPress(Sender: TObject; var Key: Char);
begin
if Key=#13 then
SelectNext(TWinControl(Sender), True, True);
end;
I've reviewed the code for FINDNEXTCONTROL and I've created a similar procedure and I've detected the problem is in these lines:
function TWinControl.FindNextControl(CurControl: TWinControl;
GoForward, CheckTabStop, CheckParent: Boolean): TWinControl;
...........
GetTabOrderList(List);
if List.Count > 0 then
begin
StartIndex := List.IndexOf(CurControl);
...........
end;
For any weird reason, procedure GETTABORDERLIST gives a list with valid references except for the current control focused, it does that LIST.INDEXOF returns -1, and the position in LIST object for current control focused is taken for another object whose NAME property is an empty string.
I developed this code:
procedure TformMain.GoNextControl(T: TWinControl; CheckTabStop: Boolean);
var
vParent, vNextChild : TWinControl;
List : TList;
CurIndex, i : Integer;
S:String;
begin
vParent:= Self; //T.Parent;
vNextChild:= nil;
List:= TList.Create;
try
vParent.GetTabOrderList(List);
if List.Count > 0 then
begin
//CurIndex:= List.IndexOf(T);
CurIndex:= -1;
for i:= 0 to List.Count-1 do begin
S:= TWinControl(List[i]).Name;
if S = EmptyStr then
begin
CurIndex:= i;
Break;
end;
end;
...........................
Anyone has a response for this anomaly or a better solution? Thanks in advance.
d
procedure TfrmMain.ControlKeyPress(Sender: TObject; var Key: Char);
begin
if Key = #13 then
begin
SelectNext(ActiveControl, TRUE, TRUE);
Key := #0;
end;
end;

Resources