Lazarus readln doesn't read the variable - pascal

I would like to ask for some help as i'm not able to identify what i did wrong in the following code as in the procedure , it doesn't read the first unit of the record , but after that it reads the uj.nev too. It only misses it for the first time which is what i can't understand ,and not only on the school computer but mine too. I would appreciate it if you could explain to me what i did wrong. Thanks in advance! (the program supposed to read records nev , varos , fizetes , and sort them into ascending order, and then write it out into a text file)
program adatbazis;
uses crt,Sysutils;
const
C_FNAME = 'adatbazis.txt';
var
a,n,j,l:integer;
tfout: Textfile;
type
Tember=record
nev:string;
varos:string;
fizetes:longint;
end;
procedure beiras(var uj:Tember);
begin
writeln('nev',j,':');
readln(uj.nev);
writeln('varos',j,':');
readln(uj.varos);
writeln('fizetes',j,':');
readln(uj.fizetes);
end;
var
i,k:integer;
seged:Tember;
tomb: array[1..20] of Tember;
begin
write('n :');
read(n);
for j:= 1 to n do begin
beiras(tomb[j]);
end;
writeln('Mi szerint legyen rendezve?');
repeat
writeln(' Nev:1 , Fizetes:2 , varos:3');
readln(l);
until l<> 1 or 2 or 3;
if l=1 then begin
For j:= 1 to n-1 do begin
for k:= 2 to n do begin
if tomb[j].nev>tomb[k].nev then
begin
seged:=tomb[j];
tomb[j]:=tomb[k];
tomb[k]:=seged;
end;
end;
end;
end;
if l=2 then begin
For j:= 1 to n-1 do begin
for k:= 2 to n do begin
if tomb[j].fizetes>tomb[k].fizetes then
begin
seged:=tomb[j];
tomb[j]:=tomb[k];
tomb[k]:=seged;
end;
end;
end;
end;
if l=3 then begin
For j:= 1 to n-1 do begin
for k:= 2 to n do begin
if tomb[j].varos>tomb[k].varos then
begin
seged:=tomb[j];
tomb[j]:=tomb[k];
tomb[k]:=seged;
end;
end;
end;
end;
AssignFile(tfout, C_FNAME);
rewrite(tfout);
for i:=1 to n do begin
writeln(tfout,tomb[i].nev,' ',tomb[i].varos,' ',tomb[i].fizetes);
end;
CloseFile(tfout);
clrscr;
writeln('done');
readln;
end.

I noticed that the essential difference between the FPC Read() and ReadLn functions when used with standard input is not very well documented (in the Delphi documentation it can be read). Therefore, the answer to your question is the following.
According to the FPC documentation, CRLF, CR and LF, all are recognized as End-Of-Line (EOL) characters, so in the following EOL stands for any of those.
When calling Read(s) where s: string; the program waits for user input until an EOL is added to the input buffer. The input characters up until (but excluding) the EOL are removed from the buffer and passed to s. The EOL is left in the input buffer.
Next call to Read(s) will immediately recognize the EOL in the input buffer, and will not wait for user input. The EOL continues to remain in the buffer, and therefore no subsequent Read(s) will wait for user input.
The first call to ReadLn(s) after a Read(s) will also immediately detect the EOL in the buffer and not wait for user input.
When calling ReadLn(s) the program waits (as in previous example) for user input until an EOL is added to the input buffer. The input characters up until (but excluding) the EOL are removed from the buffer and passed to s. Then the EOL is removed from the buffer and thrown away.
Next call to ReadLn(s) will again stop and wait for user input.
In your code you are calling Read(n) in the very beginning of the execution. Then, in beiras() you are calling ReadLn() which detects the EOL in the buffer and therefore returns immediately, but also removes the EOL from the buffer. Subsequent calls to ReadLn() will now wait for user input.
The cure is iow to change the Read()n to a ReadLn(n) in the beginning of your program.

Related

Ada - Skipping Whitespace using look_ahead

I have a procedure, that in theory, should be skipping whitespace using a look_ahead loop. Problem is, it's not working, if there's any whitespace in the input file, it is adding it to the array of records. I think my logic is correct, but could use another pair of eyes to let me know what I'm missing, and why it's not working.
PROCEDURE Read(Calc: OUT Calculation) IS
EOL: Boolean;
C: Character;
I: Integer := 1;
BEGIN
LOOP
LOOP
Look_Ahead(C, EOL);
EXIT WHEN EOL or C /= ' ';
Get(C);
END LOOP;
EXIT WHEN ADA.Text_IO.END_OF_FILE;
Look_Ahead(C, EOL);
IF Is_Digit(C) THEN
Calc.Element(I).Kind := Number;
Get(Calc.Element(I).Int_Value);
ELSE
Calc.Element(I).Kind := Symbol;
Get(Calc.Element(I).Char_Value);
END IF;
Calc.Len := Calc.Len+1;
IF Calc.Element(I).Char_Value = '=' THEN
EXIT;
END IF;
I := I+1;
END LOOP;
END Read;
EDIT: If any of the other procedures, the code for the record etc is needed for an answer, let me know and I will post it.
For Ada.Text_IO.Look_Ahead, ARM A.10.7(8) says
Sets End_Of_Line to True if at end of line, including if at end of page or at end of file; in each of these cases the value of Item is not specified. Otherwise, End_Of_Line is set to False and Item is set to the next character (without consuming it) from the file.
(my emphasis) and I think the "without consuming it" is key. Once Look_Ahead has found an interesting character, you need to call Get to retrieve that character.
I hacked this little demo together: I left end-of-file to exception handling, and I called Skip_Line once end-of-line’s been seen because just Get wasn’t right (sorry not to be more precise!).
with Ada.Text_IO;
with Ada.IO_Exceptions;
procedure Justiciar is
procedure Read is
Eol: Boolean;
C: Character;
begin
-- Instead of useful processing, echo the input to the output
-- replacing spaces with periods.
Outer:
loop
Inner:
loop
Ada.Text_IO.Look_Ahead (C, Eol);
exit Outer when Eol; -- C is undefined
exit Inner when C /= ' ';
Ada.Text_IO.Get (C); -- consume the space
Ada.Text_IO.Put ('.'); -- instead of the space for visibility
end loop Inner;
Ada.Text_IO.Get (C); -- consume the character which isnt a space
Ada.Text_IO.Put (C); -- print it (or other processing!)
end loop Outer;
Ada.Text_IO.Skip_Line; -- consume the newline
Ada.Text_IO.New_Line; -- clear for next call
end Read;
begin
loop
Ada.Text_IO.Put ("reading: ");
Read;
end loop;
exception
when Ada.IO_Exceptions.End_Error =>
null;
end Justiciar;
Usually it's better to read an entire line and parse it than to try to parse character by character. The latter is usually more complex, harder to understand, and more error prone. So I'd suggest something like
function De_Space (Source : String) return String is
Line : Unbounded_String := To_Unbounded_String (Source);
begin -- De_Space
Remove : for I in reverse 1 .. Length (Line) loop
if Element (Line, I) = ' ' then
Delete (Source => Line, From => I, Through => I);
end if;
end loop Remove;
return To_String (Line);
end De_Space;
Line : constant String := De_Space (Get_Line);
You can then loop over Line'range and parse it. Since I'm not clear if
Get(C);
Get(Calc.Element(I).Int_Value);
Get(Calc.Element(I).Char_Value);
represent 1, 2, or 3 different procedures, I can't really help with that part.

Runtime error 104 Pascal

Im very new to using files and im really struggling to fix this any help would be great.
It seems that the error is coming from my read array function but not entirely
sure i am also not to sure what the 104 error really means
thanks in advance
program ReadFromFile;
type
lineArray = array [0..19] of String;
procedure PrintArray(lines: lineArray);
var
i: Integer;
begin
for i:=0 to High(lines) do
begin
WriteLn('Text is: ', lines[i], ' Line number is: ', i);
end;
end;
function ReadArray(var myFile: TextFile):lineArray;
var
count : Integer;
lines : lineArray;
i: Integer;
begin
ReadLn(myFile, count);
for i := 0 to count do
begin
ReadLn(myFile, lines[i]);
end;
result := lines;
end;
procedure Main();
var
myFile: TextFile;
line: lineArray;
begin
AssignFile(myFile, 'mytestfile.dat');
ReWrite(myFile);
line:=ReadArray(myFile);
Close(myFile);
AssignFile(myFile, 'mytestfile.dat');
Reset(myFile);
PrintArray(line);
Close(myFile);
end;
begin
Main();
end.
You don't know what that error means. Neither do I off the top of my head. So, let's look it up in the documentation and find out. Websearch takes us here: https://www.freepascal.org/docs-html/user/userap4.html
File not open for input
Reported by Read, BlockRead, Eof, Eoln, SeekEof or SeekEoln if the file is not opened with Reset.
You have your calls to open the file the wrong way round. Call Reset to open for reading, Rewrite to open for writing.
Notes:
Looping from 0 to count will perform count + 1 iterations. I'd expect to see you looping from 0 to count - 1.
You don't check whether your array is long enough. You therefore run the risk of a buffer overrun. A dynamic array would avoid this.
It's not clear why you open the file for a second time when you print the contents to the console.
You could have looked up the error code yourself. Please take the hint to do web search the next time you encounter an error like this.

Different result of the program than was expected, maybe because of index of arrays

i have to do the following thing.
Make a program in Pascal that after has read a text with a list of nums., it will return the numb. of the nums that appear less than one times in the text.
The text that will be read from the program should be like that.
In the first line there are two nums. seperated by a space, n and m. N is the number of nums that exist, like if the text contains the numbers 1,2,3,4, n is 4 (1..n). M is how many lines follow. Every line has a couple of nums, a,b, (1=b) a and b are separated by a space.
The file that the program will make will have written on it a num., that says how many nyms are appeared less than two tims in the text.
All the nums. are Integer.
0=
I have finished it, but the problem is that at the new text that p has to be written, p is always 1, For me the problem is at the place that i have the bold letters, it might be because i in count and i in a arrays are different, how can i correct this???
Thank you in advance.
program MyProgr;
var
F: text;
t:Textfile;
a,count:array of Integer;
b:Integer;
i,int:Integer;
countnums:Integer;
n,m:String;
lin,nums:Integer;
Small,Big:Integer;
procedure DoWhatEver(S: string);
begin
val(s,int);
Write(s,' ');
for i:=Small to Big do
if (a[i]=int) then
count[i]:=count[i]+1;
end;
procedure FilltheArray;
begin
for i:=Small to Big do
a[i]:=i+1 ;
end;
procedure ProcessString;
var
Strng, S: string;
Last, P: integer;
begin
readln(F,Strng);
Last:=0;
while Last<length(Strng) do
begin
P:=Last+1;
while (P<=length(Strng)) and (Strng[P]<>' ') do
inc(P);
S:=copy(Strng,Last+1,(P-Last-1));
DoWhatEver(S);
Last:=P;
end
end;
procedure ProcessStringA;
var
Strng: string;
Last, P: integer;
begin
readln(F,Strng);
Last:=0;
while Last<length(Strng) do
begin
P:=Last+1;
while (P<=length(Strng)) and (Strng[P]<>' ') do
inc(P);
n:=copy(Strng,Last+1,(P-Last-1));
Val(n,nums);
Last:=P;
end
end;
procedure ProcessStringB;
var
Strng: string;
Last, P: integer;
begin
readln(F,Strng);
Last:=0;
while Last<length(Strng) do
begin
P:=Last+1;
while (P<=length(Strng)) and (Strng[P]<>' ') do
inc(P);
m:=copy(Strng,Last+1,(P-Last-1));
Val(m,lin);
Last:=P;
end
end;
begin
assign(F,'myfile.txt');
reset(F);
ProcessStringA;
Writeln(nums);
ProcessStringB;
Writeln(lin);
setlength(a,nums);
Small:=Low(a);
Big:=High(a);
for i:= Small to big do
count[i]:=0;
FillTheArray;
while not eof(F) do
ProcessString;
for i:=Small to Big do
begin
if count[i]=2 then
countnums:=countnums+1;
end;
Close(f);
Assign(t,'fileout.txt');
Rewrite(t);
Writeln(t,countnums);
close(t);
end.

Reading text Files - single line vs. multiple lines

I am working on a particular scenario, where I have to read from a Text File, parse it, extract meaningful information from it, perform SQL queries with the information and then produce a reponse, output file.
I have about 3000 lines of code. Everything is working as expected. However I have been thinking of a connendrum that could possibly dissrupt my project.
The text file being read (lets call it Text.txt) may consist of a single line or multiple lines.
In my case, a 'line' is identified by its segment name - say ISA, BHT, HB, NM1, etc... each segment ending is identified by a special character '~'.
Now if the file consists of multiple lines (such that each line corresponds to a single segment); say:-
ISA....... ~
NM1....... ~
DMG....... ~
SE........ ~
and so on.... then my code essentially reads each 'line' (i.e. each segment), one at a time and stores it into a temp buffer using the following command :-
ReadLn(myFile,buffer);
and then performs evaluations based on each line. Produces the desired output. No problems.
However the issue is... what if the file consists of only a single line (consisting of multiple segments), represented as:-
ISA....... ~NM1....... ~DMG....... ~SE........ ~
then with my ReadLine command I read the entire line instead of each segment, one at a time. This doesn't work for my code.
I was thinking about creating an if, else statement pair...which is based on how many lines my Txt.txt file consists of..such as:-
if line = 1:-
then extract each segment at a time...seperated by the special character '~'
perform necessary tasks (3000 lines of code)
else if line > 1:-
then extract each line at a time (corresponding to each segment)
perform necessary tasks (3000 lines of code).
now the 3000 lines of code is repeated twice and I don't find it elegant to copy and paste all of that code twice.
I would appreciate if I could get some feedback on how to possibly solve this issue, such that, regardless of a one-line file or multiple-line file...when i proceed to evaluate, i only use one segment at a time.
There are many possible ways of doing this. Which is best for you might depend on how long these files are and how important performance is.
A simple solution is to just read characters one at a time until you hit your tilde delimiter.
The routine ReadOneItem below shows how this can be done.
procedure TForm1.Button1Click(Sender: TObject);
const
FileName = 'c:\kuiper\test2.txt';
var
MyFile : textfile;
Buffer : string;
// Read one item from text file MyFile.
// Load characters one at a time.
// Ignore CR and LF characters
// Stop reading at end-of-file, or when a '~' is read
function ReadOneItem : string;
var
C : char;
begin
Result := '';
// loop continues until break
while true do
begin
// are we at the end-of-file? If so we're done
if eof(MyFile) then
break;
// read in the next character
read ( MyFile, C );
// ignore CR and LF
if ( C = #13 ) or ( C = #10 ) then
{do nothing}
else
begin
// add the character to the end
Result := Result + C;
// if this is the delimiter then stop reading
if C = '~' then
break;
end;
end;
end;
begin
assignfile ( MyFile, FileName );
reset ( MyFile );
try
while not EOF(MyFile) do
begin
Buffer := ReadOneItem;
Memo1 . Lines . Add ( Buffer );
end;
finally
closefile ( MyFile );
end;
end;
I would use a file mapping via the Win32 API CreateFileMapping() and MapViewOfFile() functions, and then just parse the raw data as-is, scanning for ~ characters and ignoring any line breaks you might encounter in between each segment. For example:
var
hFile: THandle;
hMapping: THandle;
pView: Pointer;
FileSize, I: DWORD;
pSegmentStart, pSegmentEnd: PAnsiChar;
sSegment: AnsiString;
begin
hFile := CreateFile('Path\To\Text.txt', GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
if hFile = INVALID_HANDLE_VALUE then RaiseLastOSError;
try
FileSize := GetFileSize(hFile, nil);
if FileSize = INVALID_FILE_SIZE then RaiseLastOSError;
if FileSize > 0 then
begin
hMapping := CreateFileMapping(hFile, nil, PAGE_READONLY, 0, FileSize, nil);
if hMapping = 0 then RaiseLastOSError;
try
pView := MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, FileSize);
if pView = nil then RaiseLastOSError;
try
pSegmentStart := PAnsiChar(pView);
pSegmentEnd := pSegmentStart;
I := 0;
while I < FileSize do
begin
if pSegmentEnd^ = '~' then
begin
SetString(sSegment, pSegmentStart, Integer(pSegmentEnd-pSegmentStart));
// use sSegment as needed...
pSegmentStart := pSegmentEnd + 1;
Inc(I);
while (I < FileSize) and (pSegmentStart^ in [#13, #10]) do
begin
Inc(pSegmentStart);
Inc(I);
end;
pSegmentEnd := pSegmentStart;
end else
begin
Inc(pSegmentEnd);
Inc(I);
end;
end;
if pSegmentEnd > pSegmentStart then
begin
SetString(sSegment, pSegmentStart, Integer(pSegmentEnd-pSegmentStart));
// use sSegment as needed...
end;
finally
UnmapViewOfFile(pView);
end;
finally
CloseHandle(hMapping);
end;
end;
finally
CloseHandle(hFile);
end;

Reading integer numbers in Pascal

I'm using Pascal. I have a problem when dealing with reading file.
I have a file with integer numbers. My pascal to read the file is:
read(input, arr[i]);
if my file content is 1 2 3 then it's good but if it is 1 2 3 or 1 2 3(enter here) (there is a space or empty line at the end) then my arr will be 1 2 3 0.
From what I can recall read literally reads the file as a stream of characters, of which a blank space and carriage return are, but I believe these should be ignored as you are reading into an integer array. Does your file actually contain a space character between each number?
Another approach would be to use readLn and have the required integers stored as new lines in the file, e.g.
1
2
3
I have tested the problem on Delphi 2009 console applications. Code like this
var
F: Text;
A: array[0..99] of Integer;
I, J: Integer;
begin
Assign(F, 'test.txt');
Reset(F);
I:= -1;
while not EOF(F) do begin
Inc(I);
Read(F, A[I]);
end;
for J:= 0 to I do write(A[J], ' ');
Close(F);
writeln;
readln;
end.
works exactly as you have written. It can be improved using SeekEOLN function that skips all whitespace characters; the next code does not produce wrong additional zero:
var
F: Text;
A: array[0..99] of Integer;
I, J: Integer;
begin
Assign(F, 'test.txt');
Reset(F);
I:= -1;
while not EOF(F) do begin
if not SeekEOLN(F) then begin
Inc(I);
Read(F, A[I]);
end
else Readln(F);
end;
for J:= 0 to I do write(A[J], ' ');
Close(F);
writeln;
readln;
end.
Since all that staff is just a legacy in Delphi, I think it must work in Turbo Pascal.
You could read the string into a temporary and then trim it prior to converting it.
It doesnt hurt to mention basics like what type of Pascal on what platform you're using in order that people can give a specific answer (as the article notes, there isnt a nice way OOTB in many Pascals)
If I recall there was a string function called Val that converts a string to a number...my knowledge of Pascal is a bit rusty (Turbo Pascal v6)
var
num : integer;
str : string;
begin
str := '1234';
Val(str, num); (* This is the line I am not sure of *)
end;
Hope this helps,
Best regards,
Tom.

Resources