Ada Tutorial - Chapter 24

BINARY INPUT/OUTPUT

Any useful computer program must have a way to get data into it to operate on, and a way to get the results out to the user. In part 1 of this tutorial, we studied how to get text in and out of the computer, now we will see how to get binary data in and out of the computer. Most of the binary input and output will be to or from files because the data is always in a machine readable format, not one that a human reader can easily comprehend. A block of binary data could be transmitted to a hardware device that is designed to respond to the data, or a block of data could be input from a hardware device reporting on some process. A little more will be said about that later.

BINARY DATA OUTPUT

Example program ------> e_c24_p1.ada

Examine the program named e_c24_p1.ada for an example of outputting binary sequential data. Ada gives you five data input/output library packages that are defined in the Ada 95 Reference Manual (ARM), and required to be available with every Ada compiler. They are listed by package name as follows;

You should refer to either your compiler documentation or the ARM for the definition of these packages. All definitions are found in Annex A. Spend a little time studying the procedures and functions available with each to become familiar with the capability of each package. The knowledge you have gained studying this tutorial to this point should enable you to understand most of the specifications of these five packages.

THE PACKAGE NAMED Low_Level_IO

There is another input/output package that may be available with your compiler named Ada.Low_Level_IO. This is not required by the ARM, but if it exists, it will be used for machine dependent input/output programming with your particular implementation. If it exists with your compiler, the description of how to use it will be included with your documentation, and since it will be completely different with each compiler, no attempt will be made to explain its use here.

BACK TO e_c24_p1.ada

Although this program does very little, there are several steps that must be performed to output to a binary file. They will be taken in order, so follow along closely. First we must tell the system that we wish to use the external package Ada.Sequential_IO, which we do using a with clause in line 4. Next we define a record type to illustrate one kind of data that can be output to the file, and declare a variable of the record type in line 18.

In order to use the sequential input/output package, we must instantiate a copy of it, because it is a generic package and cannot be used directly. We do this in line 15, using the data type we wish to output to the file, then add the use clause in line 16 to make the new package readily available. We need an internal file name, and we define it in line 19, but with a minor difficulty because we have an overloaded file type available. We have made Ada.Text_IO available to illustrate the problem, even though we don't use it in this program, because it contains a type named FILE_TYPE. The package named Seq_IO that we have instantiated also contains a type named FILE_TYPE, the one we wish to use. In order to tell the system which one we want, we must use the extended naming notation to differentiate between the two types. This is done in line 19 of the example program. With these steps, we are ready to begin the executable part of the program.

In a manner similar to that used for text files which we studied earlier, we create the file in line 23, in this case using the mode Out_File since we wish to write to the file. As you recall, this also ties the internal name for the file to the external name we have chosen, NAMEFILE.TXT, which must follow the conventions for our particular Ada compiler and operating system. If your operating system is substantially different, you may need to change this name. We are finally ready to actually use the output file, so we load some nonsense data into the declared record variable in lines 25 and 26, then enter a loop where we will change the Age field of the record in order to have some varying data to write to the file. This will make it more useful when we read the data from this file in another example program.

WRITING BINARY DATA TO THE FILE

We actually write data to the file in line 30, where we use the procedure Write which is part of the instantiated package we named Seq_IO earlier, but since we have defined Seq_IO in a use clause, we do not need the qualifier "dotted" to the procedure name. We mention the internal file name to tell the system which file we wish to write to, and the variable which we desire to output. The variable to be output must be of the type for which the package was instantiated, resulting in a rule that all binary records output to any given file must be of the same type. Even though they must be of the same type, they can be records of a variant record with different variants.

After writing 100 binary records, we close the file in the manner shown in line 33, and the program is complete.

We covered a lot of territory in the last few paragraphs, but it is useful information we will need in the next few example programs, and of course we will need it anytime we wish to actually use a binary file. It should be clear that we could open several files, each storing a different data type, and write to them in any order provided that we wrote the proper data type to each file.

The resulting file, named NAMEFILE.TXT, will be used to illustrate binary reading in the next two programs, so it is imperative that you compile and execute this program. After running it, you will find the file named NAMEFILE.TXT in the default directory of your system, and if you attempt to look at it with a text editor, it will have some very strange looking characters because it is a binary file. You will notice however, that much of it will be readable because of the nature of the data written to it.

READING A BINARY FILE

Example program ------> e_c24_p2.ada

The example program named e_c24_p2.ada illustrates how to read from a binary file in a sequential mode. Everything in the declaration part of the program is identical to the last program except for including the Ada.Integer_Text_IO package in lines 2 and 3 for use later.

The only differences in the executable part is the use of the Open procedure from the Seq_IO package, which uses the In_File mode of file opening. We can now read from the file we wrote in the last program, but we cannot write to it from this program. We execute a loop 100 times where we read a record from the binary file each time through the loop. The record definition is identical to the record used to write the binary file. If the record is different in structure or in data types, you may get anything upon reading, and the data probably will not appear to have anything to do with the original data written, because the bytes will be mixed around.

You may wonder what will happen if the file that we requested the system to open is not available where we expect it to be. In the spirit of Ada, you may guess that an exception will be raised and you will be correct. If the file is not available for opening, the exception Name_Error will be raised as an indicator. There are eight exceptions defined in the package IO_Exceptions that are raised for various kinds of errors.

Assuming that the file did open properly, all 100 elements are read in and those with the Age field greater than or equal to 82 are displayed to illustrate that the data really did get written. Finally, the binary file is closed and the program terminated.

WHAT ABOUT PORTABILITY?

Suppose you wrote the binary file with one Ada compiler, and attempted to read it using a different compiler. It would be indeterminate whether or not it would work, because each implementor is free to define the internal bit patterns of the various types to fit his particular compiler. Using such simple fields as those in this illustration would lead to a very good chance of portability, but using more elaborate records or arrays, would almost certainly cause incompatibility problems.

The solution to this problem is to read a file with the same compiler that was used to write it. Of course a file written in a text format, using Ada.Text_IO, is portable and can be read with a different system.

Compile and execute this program and verify that the data really did get output as desired. If you did not compile and execute the last example program, you did not generate the file named "NAMEFILE.TXT", and you cannot successfully execute this program.

RANDOM INPUT AND OUTPUT

Example program ------> e_c24_p3.ada

Examine the file named e_c24_p3.ada for an example of random input and output. Random file access means that we can output data to the file, or read data from the file just as if the file were an array. The elements of the file do not have to be accessed in order. We will illustrate all of this in this example program.

The declaration part of this program should look familiar to you since it is nearly identical to the last two example programs. The biggest difference is the use of the Ada.Direct_IO package instead of the Ada.Sequential_IO package. We instantiate a copy of this called Ran_IO, and use it to declare the internal filename, My_In_Out_File. Next, as part of the declaration part of the program, we declare a function that will be used to output some well formatted data.

READING FROM THE RANDOM FILE

Before we can do anything with the file, we must open it, and since we intend to read from and write to this file, we open it with the InOut_File mode. Of course we use the internal filename we defined in line 19, and the external filename we have already written to. In line 36, we use the procedure Read, reading from the file we have opened, and we read the data into the record variable named Myself. The thing that is really new here is the use of the number 37 as the third actual parameter of the procedure call. This tells the system that we wish to read the 37th record from the designated file. We use our Display_Record procedure to display the record read, then tell the system that we wish to read record number 25, and display it. In line 40, we don't tell the system which record we want to read explicitly, so it returns the next record, the 26th, which we display.

WRITING TO THE RANDOM FILE

We fill the three fields of the record variable with nonsense data in lines 44 through 46, and write the modified record to records 91, 96, and 97. We write to record 97 because we don't specify a record and the system will default to the next successive record number. In line 51 we call the procedure Set_Index with the value of 88, and as you may guess, it sets the record pointer to 88, which is the next record that will be read by default if no record number is stated. The loop in lines 52 through 55 read and display all records from 88 through the last, because we keep reading until we find an end of file. You will see when you compile and execute this program that we did modify records numbered 91, 96, and 97.

You will find binary Input/Output to be very useful for temporary storage of large amounts of data, but you must keep in mind that any data you write using this technique may or may not be readable by some other system. For this reason, binary output should not be used except for data that is meant to be read by another program compiled with the same Ada compiler as the data generator. Be sure to compile and execute this program.

USING HETEROGENEOUS DATA

Example program ------> e_c24_p4.ada

Examine the example program e_c24_p4.ada which contains an example of writing several types of data to a single file using the streams capability which was added to Ada 95. The package named Ada.Streams.Stream_IO contains the required entities, so it is included in context clauses in lines 3 and 4, and we declare a file object along with an access type for use with the file. Note that even though the package is included in a use clause, the extended naming notation is required in line 8 because there is also a type FILE_TYPE defined in Ada.Text_IO, and the system does not know which to select based on the available information. The extended naming notation is not required in line 9, but is given to indicate clearly that the two types work closely together.

In lines 11 through 23, we define several types and various variables for use within the executable portion of the program. They are all assigned initial values which have no significance, but provide known values for all entities.

In line 27, we create a stream with the name My_File which will allow us to write to the file named "funny.txt" in the default directory. In line 28 we cause the access variable named My_File_Access to access the file we just opened. In lines 30 through 36, we write seven different variables, which represent three different types, to the same file in an arbitrary order. The method of writing seems very strange, and it is, because it uses an attribute of the types to do the actual writing. The file is closed in line 38, and the same file is opened for reading in line 41.

The first two variables are read from the file in lines 44 and 45, being careful to read them in the proper order and using the proper type for each variable. To illustrate that the data did get read in properly, the value of Animal is displayed on the monitor for inspection. The file is closed a second time in line 52, and the program is complete.

This very limited program illustrates only one use for the streams capability provided with Ada 95. This package also provides for random input/output rather than only sequential, and the stream can be directed into memory rather than into a file, or read directly from memory.

PROGRAMMING EXERCISES

  1. Modify e_c24_p1.ada to write the same data to two different files, except when Age is between 50 and 60. During this range, one of the characters should be changed for one of the files.(Solution)
  2. Modify e_c24_p2.ada to read both files output from exercise 1 and list the differences in the two files.(Solution)
  3. Combine e_c24_p1.ada and e_c24_p2.ada in such a way that the file is written in one loop, then read back in and displayed in a successive loop.(Solution)
Advance to Chapter 25

Return to the Table of Contents


Copyright © 1988-1998 Coronado Enterprises - Last update, February 1, 1998
Gordon Dodrill - dodrill@swcp.com - Please email any comments or suggestions.