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

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.

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: Only displaying one set of array's and not the rest from file

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.

pascal adding tedit text to record

I am having problems with adding text I have entered into a tedit, into an record.
Here is the code i currently have:
procedure TForm7.AddNewQuestionClick(Sender: TObject);
var
w: integer;
QuestDesc, QuestAnsr: string;
begin
NewQuestID.text:=(GetNextQuestionID); //Increments QuestionID part of record
w:=Length(TQuestions);
SetLength(TQuestions, w+1);
QuestDesc:= NewQuestDesc.text;
QuestAnsr:= NewQuestAns.text;
TQuestionArray[w+1].Question:= QuestDesc; // Error on this line (No default property available)
TQuestionArray[w+1].Answer:= QuestAnsr;
end;
Here is the record I am trying to add to:
TQuestion = record
public
QuestionID: integer;
Question: shortstring;
Answer: shortstring;
procedure InitQuestion(anID:integer; aQ, anA:shortstring);
end;
TQuestionArray = array of TQuestion;
Any help solving this problem would be greatly appreciated.
You're missing a few things. You've declared a procedure to help initialize a new question - you should be using it.
This should get you going:
type
TQuestion = record
QuestionID: integer;
Question: ShortString;
Answer: ShortString;
procedure InitQuestion(anID: Integer; aQ, aAns: ShortString);
end;
TQuestionArray = array of TQuestion;
var
Form3: TForm3;
var
Questions: TQuestionArray;
procedure TForm7.AddNewQuestionClick(Sender: TObject);
begin
SetLength(Questions, Length(Questions) + 1);
Questions[High(Questions)].InitQuestion(GetNextQuestionID,
NewQuestDesc.Text,
NewQuestAns.Text);
end;
If you really want to do it individually setting the fields:
procedure TForm7.AddNewQuestionClick(Sender: TObject);
var
Idx: Integer;
begin
SetLength(Questions, Length(Questions) + 1);
Idx := High(Questions);
Questions[Idx].QuestionID := GetNextQuestionID;
Questions[Idx].Question := NewQuestDesc.Text;
Questions[Idx].Answer := NewQuestAns.Text;
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;

How does this program to count vowels work?

I want to understand this code, especially PROCEDURE
PROGRAM vowels;
USES crt;
{Program that counts the number of vowels in a sentence}
CONST space=' ';
maxchar=80;
TYPE vowel=(a,e,i,o,u);
VAR buffer:ARRAY[1..maxchar] of char;
vowelcount:ARRAY[vowel] of integer;
PROCEDURE initialize;
VAR ch:vowel;
BEGIN
FOR ch:=a TO u DO
BEGIN
vowelcount[ch]:=0;
END;
END;
PROCEDURE textinput;
VAR index:integer;
BEGIN
writeln('Input a sentence');
FOR index:=1 TO maxchar DO
IF eoln THEN buffer[index]:=space
ELSE read(buffer[index]);
readln;
END;
PROCEDURE analysis;
VAR index:integer;
ch:vowel;
BEGIN
index:=1;
WHILE index<>maxchar+1 DO
BEGIN
IF buffer[index] IN ['a','e','i','o','u'] THEN
BEGIN
CASE buffer[index] OF
'a':ch:=a;
'e':ch:=e;
'i':ch:=i;
'o':ch:=o;
'u':ch:=u;
END;
vowelcount[ch]:=vowelcount[ch]+1;
END;
index:=index+1;
END;
END;
PROCEDURE vowelout;
VAR ch:vowel;
BEGIN
clrscr;
writeln;
writeln(' a e i o u');
FOR ch:=a TO u DO
write(vowelcount[ch]:4);
writeln;
END;
BEGIN
initialize;
textinput;
analysis;
vowelout;
END;
Overall: Okay this code is counting the number of vowels supplied in the input string.
Lets Begin....
TYPE vowel=(a,e,i,o,u); VAR
buffer:ARRAY[1..maxchar] of char;
vowelcount:ARRAY[vowel] of integer;
This code is defining a list of the vowels in english (a,e,i,o,u).
PROCEDURE initialize; VAR ch:vowel;
BEGIN FOR ch:=a TO u DO BEGIN
vowelcount[ch]:=0; END; END;
It then defines a variable to collect the number of each vowel, called vowelcount. That variable is an array, looks sort of like this:
vowelcount[a]=0;
vowelcount[e]=0;
vowelcount[i]=0; #... etc
Then the procedure "Analysis" is defined. This takes the input from the screen (which will be called later on in the program) and steps through each letter in the input.
WHILE index<>maxchar+1 DO BEGIN IF
buffer[index] IN ['a','e','i','o','u']
THEN BEGIN CASE buffer[index] OF
'a':ch:=a; 'e':ch:=e; 'i':ch:=i;
'o':ch:=o; 'u':ch:=u; END;
If any of those letters happens to be in the list of letters than matches a vowel, then it will add one to the number in the vowelcount array above. (vowelcount[ch]:=vowelcount[ch]+1) where ch is the matched letter. As you can see this is only triggered if it is a valid vowel (IF buffer[index] IN ['a','e','i','o','u'] )
Finally. The main code of the program, or what is actually run:
BEGIN clrscr; writeln; writeln(' a e i
o u'); FOR ch:=a TO u DO
write(vowelcount[ch]:4); writeln; END;
BEGIN initialize; textinput; analysis;
vowelout; END.
This basically strings the application together, starting by clearing the screen (in a dos prompt) and then outputting the vowels onto the screen. It then adds some formatting and outputs the current count of vowelcount (as above).
It will then request your input and finally it will output the contents of vowelcount again, which has been updated with the vowelcounts from the input you made.

Resources