Conceptually similar to most extended Pascals file i/o, Ada provides a simple yet effective collection of file packages. These can be discarded and replaced by any other file package, with of course a subsequent decrease in portability. Most of the facilities provided would not be used in high performance applications directly, but may form the basis of such a file system.
So far all the I/O performed has been on directed to the standard input/output. All of the facilities provided have come from the TEXT_IO package (even the generic packages integer_io and float_io). This package provides facilities for file manipulation of textual files (files of characters) and provides facilities such as close, delete, reset,open, create etc.
Two other standard I/O packages are for storing files that consist of the one type of fixed length object, such as records, arrays, floating point numbers etc. These are the generic packages sequential_io and direct_io.
The text i/o package's main data type is the FILE_TYPE. This is the internal representation of the file. When a file is opened or created an association is made between the name and the FILE_TYPE. The object of type FILE_TYPE is used from then on to reference the file.
The procedure create creates a file and writes some data to it.
with text_io; use text_io; with int_io; use int_io; procedure demo_file_io is my_file :text_io.file_type; begin create( file => my_file, mode => out_file, name => "data.dat"); put( file => my_file, item => "numbers and their squares"); for i in 1..10 loop put(my_file,i); put(my_file," "); put(my_file,i*i); new_line(my_file); end loop; close(my_file); -- required, Ada may not close your -- open files for you end demo_file_io;
The following program reads data from one file and writes it to another, a character at a time. It should be noted that the concept of 'end of line' is different to that provided by Unix and Dos. In those systems there is simply a character that marks the end of the line which is processed as a normal character; in Ada and Pascal there is the concept of a line terminator that is not a character in the file. To read pass this terminator you need to perform a skip_line. Similarly to partition the output file into lines, the command new_line has to be given.
-- program to read a file a character at a time, and to write
-- it out a character at a time
with text_io; use text_io;
procedure read_write is
input_file :file_type;
output_file :file_type;
char :character;
begin
open(input_file,in_file,"input.dat");
create(output_file,out_file,"output.dat");
while not end_of_file(input_file) loop
while not end_of_line(input_file) loop
get(input_file,char);
put(output_file,char);
end loop;
skip_line(input_file);
new_line(output_file);
end loop;
close(input_file);
close(output_file);
end read_write;
There are various procedures to perform file manipulation. These are
Create - Creates a file with the given name and mode. Note that if the file
- has a null string, then the file is temporary and is deleted later.
Open - Opens an existing file with the given name and mode.
Delete - Deletes the appropriate file. It is an error to delete an open file.
Reset - Returns the read (or write) position to the start of the file.
As well there are functions that report on the status of the file system.
End_of_File - Returns true if we are at the end of the current file.
End_of_Line - Returns true if we are at the end of the current text line.
Is_open - Returns true if the given file is open.
Mode - Returns the mode of the given file.
Name - Returns the name (string) of the current file.
There are various other routines, it is best to examine the specifications of the package text_io (appendix B) to get a clear idea of the package.
Ada's text_io facilities rely on the exception mechanism to report errors in the creation and opening of files. For example attempting to create a file that already exists causes an exception, as does attempting to open a file that does not.
To get around this circularity the following procedure robust_open could be used. This attempts to open the file, if it fails due to the file not being there, it attempts to create it instead.
The code is, of course, subject to race conditions. This program could be interrupted after an attempt open the file, and before the create is attempted; a second process could conceivably create the file in this time.
------------------------------------------------------------
-- Attempts to open the specified file.
-- If it doesn't work then it creates it instead
with text_io; use text_io;
procedure robust_open( the_file :in out file_type;
mode :in file_mode;
name :in string) is
begin
open(the_file,mode,name);
exception
when name_error =>
create(the_file,mode,name);
end robust_open;
Another utility, the boolean function file_exists, allows students to check if the file exists. An exception (use_error) is raised if the is already open.
------------------------------------------------------------ -- Returns true if the file specified in 'name' exists. -- This works by attempting to open the file. If this -- suceeds then the file is there. with text_io; use text_io; function file_exists(name :string) return boolean is the_file:text_io.file_type; begin open(the_file, in_file, name); -- yes it worked, close the file and return true close( the_file); return true; exception when name_error => -- couldn't open the file, assume it's not there. return false; end file_exists;
The mode append_file has been added to the allowable modes for text files. As well the concept of a standard error file (c.f. Unix/C) has been added. The procedures flush have been added to enable flushing of text file buffers.
Improvements in character handling include look ahead, and get_immediate, with various options.
Generic packages for the I/O of the new modular and decimal types have been included - their specifications conform to that of the other numeric generic I/O packages.
Most large systems do not utilise text as the basis for their files. The files are usually composed of composite objects, and typically they are records. The sequential_io generic package allows us to create files whose components are any type (they must however be constrained).
The basics of the sequential_io generic package are identical to the text_io package, except that the procedures get and put are now read and write, and the procedures deal in terms of the type the package was instantiated with. Similarly the concepts of line has dissapeared, so that the function end_of_line and the procedures skip_line and new_line have also gone.
Use of the package is demonstrated below.
with sequential_io; -- generic package
with personnel_details; use person_details;
-- has record type 'personnel'
with produce_retirement_letter;
procedure sequential_demo is
package person_io is new sequential_io(personnel);
data_file :person_io.file_type;
a_person :personnel;
begin
person_io.open(data_file,in_file,"person.dat");
while not person_io.end_of_file(data_file) loop
person_io.read(data_file,a_person);
if a_person.age > 100 then
produce_retirement_letter(a_person);
end if;
end loop;
close(data_file);
end sequential_demo;
No direct access of the file is possible. The file is opened at the start and processed until you get to the end, you reset or close the file.
Ada95 has added the file mode append_file to the specifications of sequential_io.
The direct io package builds on top of the sequential io package by providing the ability to seek directly to a given record, to determine the size of the file, to determine the current index and to be able to open the file with a new mode - inout_file (read/write).
These facilities should make it possible, in conjuction with a suitable indexing package, to provide very high level file processing packages.
The following code demonstrates the use of direct files. For brevity it assumes that the employee records are stored on the basis of their employee numbers.
with direct_io; -- generic package
with personnel_details; use personnel_details;
-- has record type 'personnel'
-- has procedure display_personnel, etc
with int_io; use int_io;
with display_menu;
procedure direct_demo is
package person_io is new direct_io(personnel);
data_file :person_io.file_type;
a_person :personnel;
option :integer;
employee_no :integer;
begin
person_io.open(data_file,inout_file,"person.dat");
loop
display_menu;
get_option(option);
case option is
when 1 =>
get(employee_no);
set_index(positive_count(employee_no));
read(data_file,a_person);
display_person(a_person);
when 2 =>
get(employee_no);
set_index(positive_count(employee_no));
read(data_file,a_person);
get_new_details(a_person);
write(data_file,a_person);
when 3 =>
exit;
when others =>
put("not a great option!");
end case;
end loop;
close(data__file);
end direct_demo;