In the previous sections, we created a random-access file and wrote data to that file. In this section, we develop a program that reads the file sequentially and prints only those records that contain data. These programs produce an additional benefit. See if you can determine what it is; we’ll reveal it at the end of this section.
The istream
function read
inputs a specified number of bytes from the current position in the specified stream into an object. For example, lines 31–32 from Fig. 14.13 read the number of bytes specified by sizeof(ClientData)
from the file associated with ifstream
object inCredit
and store the data in the client
record. Function read
requires a first argument of type char *
. Since &client
is of type ClientData *
, &client
must be cast to char *
using the cast operator reinterpret_cast
.
1 // Fig. 14.13: Fig14_13.cpp
2 // Reading a random-access file sequentially.
3 #include <iostream>
4 #include <iomanip>
5 #include <fstream>
6 #include <cstdlib>
7 #include "ClientData.h" // ClientData class definition
8 using namespace std;
9
10 void outputLine( ostream&, const ClientData & ); // prototype
11
12 int main()
13 {
14 ifstream inCredit( "credit.dat", ios::in | ios::binary );
15
16 // exit program if ifstream cannot open file
17 if ( !inCredit )
18 {
19 cerr << "File could not be opened." << endl;
20 exit( EXIT_FAILURE );
21 } // end if
22
23 // output column heads
24 cout << left << setw( 10 ) << "Account" << setw( 16 )
25 << "Last Name" << setw( 11 ) << "First Name" << left
26 << setw( 10 ) << right << "Balance" << endl;
27
28 ClientData client; // create record
29
30 // read first record from file
31 inCredit.read( reinterpret_cast< char * >( &client ),
32 sizeof( ClientData ) );
33
34 // read all records from file
35 while ( inCredit && !inCredit.eof() )
36 {
37 // display record
38 if ( client.getAccountNumber() != 0 )
39 outputLine( cout, client );
40
41 // read next from file
42 inCredit.read( reinterpret_cast< char * >( &client ),
43 sizeof( ClientData ) );
44 } // end while
45 } // end main
46
47 // display single record
48 void outputLine( ostream &output, const ClientData &record )
49 {
50 output << left << setw( 10 ) << record.getAccountNumber()
51 << setw( 16 ) << record.getLastName()
52 << setw( 11 ) << record.getFirstName()
53 << setw( 10 ) << setprecision( 2 ) << right << fixed
54 << showpoint << record.getBalance() << endl;
55 } // end function outputLine
Figure 14.13 reads every record in the credit.dat
file sequentially, checks each record to determine whether it contains data, and displays formatted outputs for records containing data. The condition in line 35 uses the ios
member function eof
to determine when the end of file is reached and causes execution of the while
statement to terminate. Also, if an error occurs when reading from the file, the loop terminates, because inCredit
evaluates to false
. The data input from the file is output by function outputLine
(lines 48–55), which takes two arguments—an ostream
object and a clientData
structure to be output. The ostream
parameter type is interesting, because any ostream
object (such as cout
) or any object of a derived class of ostream
(such as an object of type ofstream
) can be supplied as the argument. This means that the same function can be used, for example, to perform output to the standard-output stream and to a file stream without writing separate functions.
What about that additional benefit we promised? If you examine the output window, you’ll notice that the records are listed in sorted order (by account number). This is a consequence of how we stored these records in the file, using direct-access techniques. Sorting using direct-access techniques is relatively fast. The speed is achieved by making the file large enough to hold every possible record that might be created. This, of course, means that the file could be occupied sparsely most of the time, resulting in a waste of storage. This is an example of the space-time trade-off: By using large amounts of space, we can develop a much faster sorting algorithm. Fortunately, the continuous reduction in price of storage units has made this less of an issue.