Is it possible to open a Text File in Pascal several times in the same program? - text-files

I am trying to make a program that allow me to read a text file and then print it in the terminal.
I just put the simplified parts below so that you see how I think it should work.
My problem is that if for example I open the file a.txt then b.txt it works.
But when I want to open a.txt again, the program stops with an error 217. Same if I want to open another file name c.txt for example. I've spent days on this problem but I do not know where it comes from. I looked on the internet and erorr 217 seems to be related to a non-existing file ? but it is not the case for me...
The error seems to occur on the 'assign' function.
To clarify :
'key' is a Char,
'map' is a two dimension dynamic array of a Record Type.
Repeat
readln(key);
name := key +'.txt';
fileLoading(name, map, maxX, maxY);
Until key = 'l';
...
procedure fileLoading (name : String; var map : PPObjet; var maxX,maxY : Integer);
var
fichier : Text;
i, j : Integer;
chaine : String;
begin
if (FileExists(name)) then
begin
assign(fichier, name);
reset(fichier);
read(fichier,maxX);
readln(fichier,maxY);
if (maxX < 1) or (maxX > MAX) or (maxY < 1) or (maxY > MAX) then
begin
writeln('Tailles invalides');
halt();
end;
allocationTab(maxX, maxY, map);
while (not eof(fichier)) do
begin
for j := 1 to maxY do
begin
readln(fichier,chaine);
for i := 1 to maxX do
begin
case chaine[i] of
'0' : begin
map[j][i].solide := false;
map[j][i].nature := 'v';
map[j][i].valeur := chaine[i];
end;
'1' : begin
map[j][i].solide := true;
map[j][i].nature := 'm';
map[j][i].valeur := chaine[i];
end;
'2'..'9' : begin
map[j][i].solide := false;
map[j][i].nature := 's';
map[j][i].valeur := chaine[i];
end;
end;
end;
end;
end;
end
else
begin
writeln('Erreur le fichier n''existe pas');
halt();
end;
close(fichier);
end;
...
This is the first time I ask a question on stack overflow and I'm not really familiar with it, so I hope my problem is clear enough, as well as my english.
Thanks in advance for all the help you may bring.

try setting
filemode:=0;
before your assign/reset

Related

Wrong use of 'file of char'

Im having problems with this code, I have two file of char, one is filed with information about books, and the other is empty, i have to write in SAL some information from S and then show the total of how many books match the first 2 digits of the code and how many are R and how many are T. The code, does write the information form S to Sal, but when its supposed to show the totals it appears ERORR 100 on screen. I read about it and it says that it is a problem with 'Disk read error' and that *This error typically occurs, if you "seed" a non-existent record of a typed file and try to read/write it. *, i really dont undertand.
I've benn trying to figure it out, but I haven't been able to. I notice that if I dont put 'WHILE NOT EOF(S) DO' the error does not appear, but of course i need the while, if someone is able to point out my mistakes i would really apreciate it.
This is the code:
uses crt;
var
i : byte;
s,sal: file of char;
v,l1,l2: char;
cs,cn,cl: integer;
pn,ps,tot: integer;
BEGIN
cs:=0; cn:=0; i:=0; cl:=0;
Assign (s, 'C:\Users\te\Documents\s.txt');
{$I-}
Reset (s);
{$I+}
if IOResult <> 0 then
begin
writeln('Error');
halt(2);
end;
Assign (sal, 'C:\Users\te\Documents\sal.txt');
{$I-}
Rewrite (sal);
IOResult;
{$I+}
if IOResult <> 0 then
halt(2);
writeln('Please write the code of the book, only 2 digits');
read(L1);read(L2);
read(s,v);
while (not eof(s)) do
begin
for i:=1 to 2 do
read(s,v);
if (v = '0') then
begin
read(s,v);
if (v = '1') or (v = '2') then
begin
for i:=1 to 5 do
read(s,v);
if (v = 'R') then
begin
read(s,v);
cs:= cs + 1;
end
else
begin
if (v = 'T') then
begin
cn:= cn + 1;
read(s,v);
end;
end;
while (v <> '-') do
read(s,v);
while (v = '-') do
read(s,v);
if (v = L1) then
begin
write(sal, v);
read(s,v);
if (v = L2) then
begin
write(sal,v);
read(s,v);
cl:= cl + 1;
end;
end;
while ( v <> '/') do
begin
write(sal,v);
read(s,v);
end;
write(sal, '-');
end
else
begin
for i:= 1 to 5 do
read(s,v);
if (v = 'R') then
cs:= cs + 1
else
cn:= cn + 1;
if (v = L1) then
read(s,v);
if (v = L2) then
begin
cl:= cl + 1;
read(s,v);
end;
end;
end
else
begin
for i:= 1 to 5 do
read(s,v);
if (v = 'R') then
cs:= cs + 1
else
cn:= cn + 1;
if (v = L1) then
read(s,v);
if (v = L2) then
begin
cl:= cl + 1;
read(s,v);
end;
end;
end;
tot:= cs + cn;
ps:= (cs * 100) div tot;
pn:= (cn * 100) div tot;
writeln('TOTAL ',cl);
writeln();
writeln(ps,'% and',pn,'%');
The file S content:
02022013Rto kill a mockingbird-1301/02012014Tpeter pan-1001/02032013Thowto-2301/02012012Tmaze runner-1001/02012012Tmaze runner-1001/02012012Tmaze runner-1001/$
I really just need someone else's point of view on this code, I think maybe the algorithm is flawed.
Thanks
(After your edit, i see that your code now compiles w/o error in FPC, so I'm glad you've managed to fix the error yourself)
As this is obviously coursework, I'm not going to fix your code for you and in any case the wayEven so, I'm afraid you are going about this is completely wrong.
Basically, the main thing wrong with your code is that you are trying to control what happens as your read the source file character by character. Quite frankly, that's a hopeless way of trying to do it, because it makes the execution flow unnecessarily complicated and littered with ifs, buts and loops. It also requires you to keep mental track of what you are trying to do at any given step, and the resulting code is inherently not self-documenting - imagine if you came back to your code in six months, could you tell at a glance how it works and what it does? I certsinly couldn't personally.
You need to break the task down in a different way. Instead of analysing the problem from the bottom up ("If I read this character next, then what I need to do next is ...') do it from the top down: Although your input file is a file of char, it contains a series of strings, separated by a / character and finally terminated by a $ (but this terminator does not really matter). So what you need to do is to read these strings one-by-one; once you've got one, check whether it's the one you're looking for: if it is. process it however you need to, otherwise read the next one until you reach the end of the file.
Once you have successfully read one of the book strings, you can then split it up into the various fields it's composed of. The most useful function for doing this splitting is probably Copy, which lets you extract substrings from a string - look it up in the FPC help. I've included functions ExtractTitle and ExtractPreamble which show you what you need to do to write similar functions to extract the T/R code and the numeric code which follows the hyphen. Btw, if you need to ask a similar q in the future, it would be very helpful if you include a description of the layout and meaning of the various fields in the file.
So, what I'm going to show you is how to read the series of strings in your S.Txt by building them character-by-character. In the code below, I do this using a function GetNextBook which I hope is reasonable self-explanatory. The code uses this function in a while loop to fill the BookRecord string variable. Then, it simply writes the BookRecord to the console. What your code should do, of course, is to process the BookRecord contents to see if it is the one you are looking for and then do whether the remainder of your task is.
I hope you will agree that the code below is a lot clearer, a lot shorter and will be a lot easier to extend in future than the code in your q. They key to structuring a program this way is to break the program's task into a series of functions and procedures which each perform a single sub-task. Writing the program that way makes it easier to "re-wire" the program to change what it does, without having to rewrite the innards of the functions/procedures.
program fileofcharproject;
uses crt;
const
sContents = '02022013Rto kill a mockingbird-1301/02012014Tpeter pan-1001/02032013Thowto-2301/02012012Tmaze runner-1001/02012012Tmaze runner-1001/02012012Tmaze runner-1001/$';
InputFileName = 'C:\Users\MA\Documents\S.Txt';
OutputFileName = 'C:\Users\MA\Documents\Sal.Txt';
type
CharFile = File of Char; // this is to permit a file of char to be used
// as a parameter to a function/procedure
function GetNextBook(var S : CharFile) : String;
var
InputChar : Char;
begin
Result := '';
InputChar := Chr(0);
while not Eof(S) do begin
Read(S, InputChar);
// next, check that the char we've read is not a '/'
// if it is a '/' then exit this while loop
if (InputChar <> '/') then
Result := Result + InputChar
else
Break;
end;
end;
function ExtractBookTitle(BookRecord : String) : String;
var
p : Integer;
begin
Result := Copy(BookRecord, 10, Length(BookRecord));
p := Pos('-', Result);
if p > 0 then
Result := Copy(Result, 1, p - 1);
end;
procedure AddToOutputFile(var OutputFile : CharFile; BookRecord : String);
var
i : Integer;
begin
for i := 1 to Length(BookRecord) do
write(OutputFile, BookRecord[i]);
write(OutputFile, '/');
end;
function ExtractPreamble(BookRecord : String) : String;
begin
Result := Copy(BookRecord, 1, 8);
end;
function TitleMatches(PartialTitle, BookRecord : String) : Boolean;
begin
Result := Pos(PartialTitle, ExtractBookTitle(BookRecord)) > 0;
end;
var
i : Integer; //byte;
s,sal: file of char;
l1,l2: char;
InputChar : Char;
BookFound : Boolean;
cs,cn,cl: integer;
pn,ps,tot: integer;
Contents : String;
BookRecord : String;
PartialTitle : String;
begin
// First, create S.Txt so we don't have to make any assumptions about
// its contents
Contents := sContents;
Assign(s, InputFileName);
Rewrite(s);
for i := 1 to Length(Contents) do begin
write(s, Contents[i]); // writes the i'th character of Contents to the file
end;
Close(s);
cs:=0; cn:=0; i:=0; cl:=0;
// Open the input file
Assign (s, InputFileName);
{$I-}
Reset (s);
{$I+}
if IOResult <> 0 then
begin
writeln('Error');
halt(2);
end;
// Open the output file
Assign (sal, OutputFileName);
{$I-}
Rewrite (sal);
IOResult;
{$I+}
if IOResult <> 0 then
halt(2);
// the following reads the BookRecords one-by-one and copies
// any of them which match the partial title to sal.txt
writeln('Enter part of a book title, followed by [Enter]');
readln(PartialTitle);
while not Eof(s) do begin
BookRecord := GetNextBook(S);
writeln(BookRecord);
writeln('Preamble : ', ExtractPreamble(BookRecord));
writeln('Title : ', ExtractBookTitle(BookRecord));
if TitleMatches(PartialTitle, BookRecord) then
AddToOutputFile(sal, BookRecord);
end;
// add file '$' to sal.txt
write(sal, '$');
Close(sal);
Close(s);
writeln('Done, press any key');
readln;
end.

Read / write different Records data from / to Untyped files in Pascal?

I've a programming project in my college.
Using a File type for storing data is allowed, and I did exactly like this one: pascal-programming
And, here's what I achieved so far:
I tried to write the Records data into Untyped files instead and it worked
I want to override a function with dynamic parameter (e.g: I can switch which Record I want to process, in this case there's 2 different "Records").
Open(var f: File; var data)
data = represent can receive "anything". cmiiw
The reason why I did this, I don't think it's a good idea to recreate the same function over and over, e.g: when using 3 or more different "Records"
I also encountered a problem that the files can't store or backup the actual binary files to the temporary "Records" variable, it always give the 0 values.
go to my github source code
my solution here doesn't provide any generic related procedures (check the last sentences):
program test_untyped;
{ A crude database recording }
uses crt;
type
Temployee = record
name : string[20];
address : string[40];
phone : string[15];
age : byte;
salary : longint;
end;
arr_employee = array[1..100] of Temployee;
var
F : File;
c : char;
// r : Temployee;
r, realR : arr_employee;
s : string;
i, j, n : integer;
procedure fRead;
begin
seek(F, 0);
i := 0;
repeat
clrscr;
inc(i);
writeln('increment: ', i); readln;
writeln('File position : ',filepos(F));
blockRead(F, r[i], sizeOf(Temployee));
writeln('Name = ', r[i].name); { Input data }
writeln('Address = ', r[i].address);
writeln('Phone = ', r[i].phone);
writeln('Age = ', r[i].age);
writeln('Salary = ', r[i].salary);
write('Show data again (Y/N) ?');
repeat
c:=upcase(readkey); { Ask user : Input again or not }
until c in ['Y','N'];
writeln(c);
// realR[i] := r[i]; // backup, to show later
until c='N';
end; // end fRead
procedure fWrite;
begin
seek(F, filesize(F));
repeat
clrscr;
inc(i);
writeln('berapa nilai i: ', i);
writeln('File position : ',filepos(F));
write('Name = '); readln(r[i].name); { Input data }
write('Address = '); readln(r[i].address);
write('Phone = '); readln(r[i].phone);
write('Age = '); readln(r[i].age);
write('Salary = '); readln(r[i].salary);
blockWrite(F, r[i], sizeOf(Temployee)); { Write data to file }
write('Input data again (Y/N) ?');
repeat
c:=upcase(readkey); { Ask user : Input again or not }
until c in ['Y','N'];
writeln(c);
until c='N';
end;
// procedure fDelete;
// var
// nama: string;
// delElement: integer;
// tempR: Temployee;
// begin
// seek(F, 0);
// write('search your data by name: '); readln(nama);
// while not eof(F) do
// begin
// writeln('file position: ', filePos(F));
// blockRead(F, tempR, sizeOf(Temployee));
// if (nama = tempR.name) then
// begin
// delElement := filePos(F);
// end else
// begin
// // seek(F, )
// blockWrite(F, tempR, sizeOf(Temployee));
// end;
// end;
// end; // end fDelete
procedure fDisplay;
begin
writeln('nilai i saat ini: ', i); readln;
for j := 1 to i do
begin
clrscr;
writeln('Name = ', r[j].name); { Input data }
writeln('Address = ', r[j].address);
writeln('Phone = ', r[j].phone);
writeln('Age = ', r[j].age);
writeln('Salary = ', r[j].salary);
readln;
end;
end;
begin
clrscr;
// write('Input file name to record databases : '); readln(s);
s := 'coba1.dat';
assign(F,s); { Associate it }
{$I-}
reset(F, sizeOf(Temployee)); { First, open it }
{$I+}
n:=IOResult;
if n<>0 then { If it's doesn't exist then }
begin
{$I-}
rewrite(F, sizeOf(Temployee)); { Create it }
{$I+}
n:=IOResult;
if n<>0 then
begin
writeln('Error creating file !'); halt;
end;
end
else
begin { If it exists then }
n:=filesize(F); { Calculate total record }
// seek(F,n); { Move file pointer PAST the last record }
end;
fileMode := 2;
reset(F, sizeOf(Temployee));
fRead;
fWrite;
// fDelete;
fDisplay;
close(F);
end.
I'm wondering is the Pascal can be any good to use a generic programming? at least for this semester using Pascal in my college XD
Thank you and Best Regards,
EDIT:
Pascal still doesn't support Generic Programming 'till the day I posted this question. So sad, really.
You might wanna consider read this references instead.
I don't understand the main issue here, but would suggest using a typed file instead of an untyped one.
An untyped file is much harder to maintain, and provides (in my eyes) no benefits.
Consider the code:
type
Temployee = record
name : string[20];
address : string[40];
phone : string[15];
age : byte;
salary : longint;
end;
VAR
fEmployee : File Of Temployee;
Employees : ARRAY[0..100] Of Temployee;
Employee : Temployee;
PROCEDURE OpenEmployeeFile(CONST TheFileName:AnsiString);
BEGIN
AssignFile(fEmployee,TheFileName);
IF FileExistsUTF8(TheFileName) { *Converted from FileExists* }
THEN Reset(fEmployee)
ELSE Rewrite(fEmployee);
END;
PROCEDURE CloseEmployeeFile;
BEGIN
Close(fEmployee);
END;
FUNCTION ReadEmployee(Position:WORD): Temployee;
BEGIN
Seek(fEmployee,Position);
Read(fEmployee,Result);
END;
PROCEDURE WriteEmployee(CONST Employee:Temployee; Position:WORD);
BEGIN
Seek(fEmployee,Position);
Write(fEmployee,Employee);
END;
Error handling not implemented.
Code samples as a guideline, not complete.
It provides a basic skeleton for opening and closing the employee-file, as well as reading and writing at specific positions (specific records) in the file.
Open file.
Write all the records you want.
Close file.
Or.
Open file.
Read all the records you want.
Close file.

Pascal Segmentation Fault parsing Text File

I am working on a Question/Answer UI application in Pascal / Lazarus. My problem is that upon invoking below code through a button click, the program crashes with a Segmentation Fault error.
// more declarations... (UI Form, Buttons, ...)
type
TQuestion = class(TObject)
title: string;
answers: array of string;
correct: integer;
end;
var
questions: array of TQuestion;
procedure TForm1.BStartClick(Sender: TObject);
var
i: integer;
j: integer;
line: string;
arrayLength: integer;
question: TQuestion;
stringList: TStringList;
begin
stringList := TStringList.create;
stringList.LoadFromFile('questions.txt');
for i := 0 to stringList.Count - 1 do ;
begin
line := stringList[i];
if (length(line) >= 2) then
if (line[2] = ' ') and ((line[1] = '-') or (line[1] = '+')) then
begin
arrayLength := length(question.answers);
SetLength(question.answers, arrayLength + 1);
question.answers[arrayLength] :=
Copy(line, 2, Length(line) - 1);
if zeile[1] = '+' then
question.correct := arrayLength;
end
else
begin
question := TQuestion.Create;
question.title := line;
arrayLength := length(questions);
setLength(questions, arrayLength + 1);
questions[arrayLength] := question;
end;
end;
BStart.Visible := False;
end;
Well, my Pascal knowledge goes to 10 to 15 years ago. However, I can see that you have an extra semicolon at the end of this line:
for i := 0 to stringList.Count - 1 do ;

Pascal error 'call by var for arg no.1 has to match exactly'

I learning to make a program that gets data from a txt file and places it in arrays.
the following are its types :
type
ekspedisi = record
nmeksp : string; // Nama Ekspedisi
jlp : string; // Jenis layanan pengiriman
biaya : integer; // Biaya pengiriman per kg
lp : integer; // per hari
end;
ekspedisiku = record
nom : array [1..100] of ekspedisi;
end;
and a simple algorithm
procedure getDaftarEkspedisi(var kirim : ekspedisiku);
var
i,j,k : integer;
eksp : text;
init : string;
garis : array [1..100] of integer;
mark : string;
jeks : integer;
count : integer;
begin
assign(eksp,'ekspedisi.txt');
reset(eksp);
i := 0;
k := 1;
j := 1;
mark := '|';
jeks := 10;
writeln('Loading ekspedisi.. ');
while(not(eof(eksp))) do
begin
readln(eksp,init);
i := i + 1;
for j := 1 to length(init) do
begin
if init[j] = mark then
begin
garis[k] := j;
k := k + 1;
end;
end;
for i := 1 to jeks do
begin
count := ((i-1)*5);
kirim.nom[i].nmeksp := copy(init,garis[1+count] + 2,garis[2+count]-garis[1+count]-2);
kirim.nom[i].jlp := copy(init,garis[2+count] + 2,garis[3+count]-garis[2+count]-2);
val(copy(init,garis[3+count] + 2,garis[4+count]-garis[3+count]-2),kirim.nom[i].biaya);
val(copy(init,garis[4+count] + 2,garis[5+count]-garis[4+count]-2),kirim.nom[i].lp);
end;
close(kirim);
writeln('loading sukses.');
end;
end;
from that code, i get the following error
<166,13>Error: Call by var for arg no.1 has to match exactly : got "ekspedisiku" expected "Text"
curiously, line 166 is only
close(kirim);
any help is appreciated.
You need to pass the file handle to close, so:
close(kirim);
should be:
close(eksp);
It also looks like you're closing the file at the wrong place in your function. It should most likely be after the while loop, so you need to change:
close(kirim);
writeln('loading sukses.');
end;
end;
to:
end;
close(kirim);
writeln('loading sukses.');
end;
Note that this mistake probably happened because your identation is messed up - if you're careful with formatting your code properly then you won't be so likely to make this kind of error.

Lazarus display numbers from memo to for exampel lisbox

I have these data:
CMD210 STA_ 99.0 uS Temp 22.1 C
CMD210 STAB 99.9 uS Temp 22 C
CMD210 STAB 0.1 mS Temp 22.1 C
CMD210 STA_ 99.5 uS Temp 22.1 C
CMD210 STAB 99.4 uS Temp 22 C
CMD210 ST__ 99.0 uS Temp 22.2 C
CMD210 STAB 0.1 mS Temp 22 C
CMD210 STAB 99.3 uS Temp 22.2 C
I would like to have a program that display the temperature from memo for exampel in a listbox.
I know I have to get loop and something with 2 char with 'p' and 'c', because the number is between those to letters....
procedure TForm1.Button4Click(Sender: TObject);
var
midlet,midler:char;
resultat,x:integer;
linecount,index:integer;
found: boolean;
begin
midlet:= 'p';
midler:='C';
index:=0;
resultat:=midlet+x+midler
found := false;
linecount := Memo1.lines.count;
while index<= linecount - 1 do
begin
if x = memo1.lines[index] then
found := true;
index :=index + 1;
end
if found = true then
ListBox1.text:= floattostrF(x,ffFixed,15,2);
end;
There are several problems in your example so this answer will be limited to "how extracting and converting the temperature from a line". You have fundamentally two ways to achieve the task:
use the regular expressions.
write a custom parser.
the custom parser is quite easy to write:
accumulate non-blank chars in an identifier.
if the identifier is equal to Temp then define a flag.
convert the identifier to a double if the flag is defined and if someting's been accumulated.
example:
program Project1;
uses
sysutils;
const
line1 = 'CMD210 STAB 99.3 uS Temp 22.2 C';
line2 = 'CMD210 STAB 0.1 mS Temp 22 C';
line3 = 'it is quite hot over there Temp 55.123456 C';
line4 = 'bla bla bla bla 12.564 C';
line5 = '';
function getTemperature(aLine: string): double;
var
reader: PChar;
identifier: string;
AccumulateTemp: boolean;
const
_Nan = 0/0;
begin
// initialize local variables.
identifier := '';
AccumulateTemp := false;
// success can be tested with isNan()
result := _Nan;
// add a distinct terminal char:
aLine := aLine + #0;
reader := #aLine[1];
while(true) do begin
if reader^= #0 then
exit;
// blank: test the identifier
if reader^ in [#9, ' '] then
begin
if AccumulateTemp then
begin
if not TryStrToFloat(identifier, result) then
result := _Nan;
AccumulateTemp := false;
exit;
end;
if identifier = 'Temp' then
AccumulateTemp := true;
identifier := '';
end else
// accumulate
identifier := identifier + reader^;
Inc(reader);
end;
end;
begin
DecimalSeparator := '.';
writeln( format('%.7f', [getTemperature(line1)]) );
writeln( format('%.7f', [getTemperature(line2)]) );
writeln( format('%.7f', [getTemperature(line3)]) );
writeln( format('%.7f', [getTemperature(line4)]) );
writeln( format('%.7f', [getTemperature(line5)]) );
readln;
end.
which outputs
22.2000000
22.0000000
55.1234560
Nan
Nan

Resources