When using the procedure Set_Line (File, X) after using the procedure Skip_Line(File, Y) the error, END.ERROR is raised when X <= Y
(except when X = Y = 1).
When the procedure Set_Line (File, X) is called on a File of mode IN_FILE, it internally calls Skip_Line until the file line number reaches X.
If X is not equal than the current line in the file, the procedure will continuously skip lines until it reaches another page with a matching line. (RM 2022 p.g.523)
I am curious about the reasoning for this functionality as it has caused an amusing problem in the trivial program I am writing.
The program will get the number of lines from a .txt file and select a random line to print to the console. A word of the day program.
Minimal Example of Issue:
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
File_Name : constant String := "TEST.txt";
Test_File : File_Type;
Create (Test_File, Out_File, File_Name);
for I in 1 .. 10 loop
Put_Line (Test_File, "Line :" & Integer'Image (I));
Open (Test_File, In_File, "Test.txt");
Skip_Line (Test_File, 5);
Set_Line (Test_File, 4);
--Will raise End_Error
I would think that the procedure would check if X < File.Line then perform further actions. While am able to work around this, I am interested to understand the reasoning.
When you open a file F, the current line number is 1. If you then call Skip_Line(F, N), the current line number becomes N+1, not N. Therefore, if you then call Set_Line(F, N), N is LESS than the current line number, and so the program reads forward in the file until it finds a page terminator, where the current line number count starts over from 1, and then reads forward until line number N in that page. If the file has no page terminators (or no later page with at least N lines) the program reads until it reaches the end of the file, and then raises End_Error.
Most text files (today) have no page terminators. For such a file, calling Set_Line with an argument less than the current line number should always raise End_Error.
The special case “Skip_Line(F, 1); Set_Line(F, 1)” indeed seems to omit the End_Error, with GNAT, but only if you do not try to read anything from the file after the Set_Line. This seems to be a fairly harmless glitch in GNAT, but does violate the Ada RM, IMO.
Why do you use Skip_Line at all? If you want to advance to line number N, just Set_Line(F, N) is enough.
Btw, it is better, and customary, to refer to Ada RM text by the section number and paragraph number, not the page number. One reason is that the most accessible Ada RM is the web-based one, where there are no page numbers. So the description of what Set_Line does for an In_File is best referred to as RM A.10.5(39-40): “If the mode is In_File: Has the effect of repeatedly calling Skip_Line…”. See http://www.ada-auth.org/standards/22rm/html/RM-A-10-5.html
You can link to the para by
n is the para number (without any text after a
/, so that (37/3) would be linked as just
http://www.ada-auth.org/standards/22rm/html/RM-A-10-5.html#p37 (which, unhelpfully for my point, is rendered like this here: Operations on Columns, Lines, and Pages - unlike Niklas’s above)
Thanks for the helpful linking advice, Simon.
Re the rendering of the link: at first I, too, got the not-very-helpful automatically fetched section heading as the rendering, but then I used the “Hyperlink” entry on the menu bar of the reply-composition menu where one can separately enter the URL and the text to be shown.
Thank you for the reply, the reason for using skip line in the original program was to count the total number of lines in the .txt file. The program flow was essentially:
Use counter and skip line to count lines in file
Generate random number from 1 to line count - 1, read text at that line.
The problem essentially arrives because I am skipping though the whole file to count the lines at which point using Set_Line will always result in END_ERROR (except for 1).
while not End_Of_File (Test_File) loop
Line_Count := Line_Count + 1;
However, now that I look at the Ada RM more carefully, I see that using Skip_Line in this way will result in the page number being incremented and the line/col number being set to 1. Which I find odd as the page length for the file is the default 0 and as such should be unbounded and not incremented at this stage. Or at least not for a file this small?
Anyways, I can work around this, thank you for the help. =)
Only if the line terminator is immediately followed by a page terminator
Note that Set_Line only lets you go forward in the file, not backward. After counting the total number of lines with your Skip_Line loop, call Reset (F) to return to the start of the file, then Set_Line to advance to the line you want to read and display.
On my system, your program raises Name_Error when trying to open “Test.txt”, since no such file exists.
Ah, my bad sorry. The capitalisation is different beteen the file name and the name passed as a parameter to Open. Perhaps an OS difference as it unintentionally works for me.