We now present a substantial transaction-processing program (Fig. 14.14) using a random-access file to achieve instant-access processing. The program maintains a bank’s account information. It updates existing accounts, adds new accounts, deletes accounts and stores a formatted listing of all current accounts in a text file. We assume that the program of Fig. 14.11 has been executed to create the file credit.dat
and that the program of Fig. 14.12 has been executed to insert the initial data. Line 25 opens the credit.dat
file by creating an fstream
object for both reading and writing in binary format.
1 // Fig. 14.14: Fig14_14.cpp
2 // This program reads a random-access file sequentially, updates
3 // data previously written to the file, creates data to be placed
4 // in the file, and deletes data previously stored in the file.
5 #include <iostream>
6 #include <fstream>
7 #include <iomanip>
8 #include <cstdlib>
9 #include "ClientData.h" // ClientData class definition
10 using namespace std;
11
12 int enterChoice();
13 void createTextFile( fstream& );
14 void updateRecord( fstream& );
15 void newRecord( fstream& );
16 void deleteRecord( fstream& );
17 void outputLine( ostream&, const ClientData & );
18 int getAccount( const char * const );
19
20 enum Choices { PRINT = 1, UPDATE, NEW, DELETE, END };
21
22 int main()
23 {
24 // open file for reading and writing
25 fstream inOutCredit( "credit.dat", ios::in | ios::out | ios::binary );
26
27 // exit program if fstream cannot open file
28 if ( !inOutCredit )
29 {
30 cerr << "File could not be opened." << endl;
31 exit ( EXIT_FAILURE );
32 } // end if
33
34 int choice; // store user choice
35
36 // enable user to specify action
37 while ( ( choice = enterChoice() ) != END )
38 {
39 switch ( choice )
40 {
41 case PRINT: // create text file from record file
42 createTextFile( inOutCredit );
43 break;
44 case UPDATE: // update record
45 updateRecord( inOutCredit );
46 break;
47 case NEW: // create record
48 newRecord( inOutCredit );
49 break;
50 case DELETE: // delete existing record
51 deleteRecord( inOutCredit );
52 break;
53 default: // display error if user does not select valid choice
54 cerr << "Incorrect choice" << endl;
55 break;
56 } // end switch
57
58 inOutCredit.clear(); // reset end-of-file indicator
59 } // end while
60 } // end main
61
62 // enable user to input menu choice
63 int enterChoice()
64 {
65 // display available options
66 cout << "
Enter your choice" << endl
67 << "1 - store a formatted text file of accounts" << endl
68 << " called "print.txt" for printing" << endl
69 << "2 - update an account" << endl
70 << "3 - add a new account" << endl
71 << "4 - delete an account" << endl
72 << "5 - end program
? ";
73
74 int menuChoice;
75 cin >> menuChoice; // input menu selection from user
76 return menuChoice;
77 } // end function enterChoice
78
79 // create formatted text file for printing
80 void createTextFile( fstream &readFromFile )
81 {
82 // create text file
83 ofstream outPrintFile( "print.txt", ios::out );
84
85 // exit program if ofstream cannot create file
86 if ( !outPrintFile )
87 {
88 cerr << "File could not be created." << endl;
89 exit( EXIT_FAILURE );
90 } // end if
91
92 // output column heads
93 outPrintFile << left << setw( 10 ) << "Account" << setw( 16 )
94 << "Last Name" << setw( 11 ) << "First Name" << right
95 << setw( 10 ) << "Balance" << endl;
96
97 // set file-position pointer to beginning of readFromFile
98 readFromFile.seekg( 0 );
99
100 // read first record from record file
101 ClientData client;
102 readFromFile.read( reinterpret_cast< char * >( &client ),
103 sizeof( ClientData ) );
104
105 // copy all records from record file into text file
106 while ( !readFromFile.eof() )
107 {
108 // write single record to text file
109 if ( client.getAccountNumber() != 0 ) // skip empty records
110 outputLine( outPrintFile, client );
111
112 // read next record from record file
113 readFromFile.read( reinterpret_cast< char * >( &client ),
114 sizeof( ClientData ) );
115 } // end while
116 } // end function createTextFile
117
118 // update balance in record
119 void updateRecord( fstream &updateFile )
120 {
121 // obtain number of account to update
122 int accountNumber = getAccount( "Enter account to update" );
123
124 // move file-position pointer to correct record in file
125 updateFile.seekg( ( accountNumber - 1 ) * sizeof( ClientData ) );
126
127 // read first record from file
128 ClientData client;
129 updateFile.read( reinterpret_cast< char * >( &client ),
130 sizeof( ClientData ) );
131
132 // update record
133 if ( client.getAccountNumber() != 0 )
134 {
135 outputLine( cout, client ); // display the record
136
137 // request user to specify transaction
138 cout << "
Enter charge (+) or payment (-): ";
139 double transaction; // charge or payment
140 cin >> transaction;
141
142 // update record balance
143 double oldBalance = client.getBalance();
144 client.setBalance( oldBalance + transaction );
145 outputLine( cout, client ); // display the record
146
147 // move file-position pointer to correct record in file
148 updateFile.seekp( ( accountNumber - 1 ) * sizeof( ClientData ) );
149
150 // write updated record over old record in file
151 updateFile.write( reinterpret_cast< const char * >( &client ),
152 sizeof( ClientData ) );
153 } // end if
154 else // display error if account does not exist
155 cerr << "Account #" << accountNumber
156 << " has no information." << endl;
157 } // end function updateRecord
158
159 // create and insert record
160 void newRecord( fstream &insertInFile )
161 {
162 // obtain number of account to create
163 int accountNumber = getAccount( "Enter new account number" );
164
165 // move file-position pointer to correct record in file
166 insertInFile.seekg( ( accountNumber - 1 ) * sizeof( ClientData ) );
167
168 // read record from file
169 ClientData client;
170 insertInFile.read( reinterpret_cast< char * >( &client ),
171 sizeof( ClientData ) );
172
173 // create record, if record does not previously exist
174 if ( client.getAccountNumber() == 0 )
175 {
176 string lastName;
177 string firstName;
178 double balance;
179
180 // user enters last name, first name and balance
181 cout << "Enter lastname, firstname, balance
? ";
182 cin >> setw( 15 ) >> lastName;
183 cin >> setw( 10 ) >> firstName;
184 cin >> balance;
185
186 // use values to populate account values
187 client.setLastName( lastName );
188 client.setFirstName( firstName );
189 client.setBalance( balance );
190 client.setAccountNumber( accountNumber );
191
192 // move file-position pointer to correct record in file
193 insertInFile.seekp( ( accountNumber - 1 ) * sizeof( ClientData ) );
194
195 // insert record in file
196 insertInFile.write( reinterpret_cast< const char * >( &client ),
197 sizeof( ClientData ) );
198 } // end if
199 else // display error if account already exists
200 cerr << "Account #" << accountNumber
201 << " already contains information." << endl;
202 } // end function newRecord
203
204 // delete an existing record
205 void deleteRecord( fstream &deleteFromFile )
206 {
207 // obtain number of account to delete
208 int accountNumber = getAccount( "Enter account to delete" );
209
210 // move file-position pointer to correct record in file
211 deleteFromFile.seekg( ( accountNumber - 1 ) * sizeof( ClientData ) );
212
213 // read record from file
214 ClientData client;
215 deleteFromFile.read( reinterpret_cast< char * >( &client ),
216 sizeof( ClientData ) );
217
218 // delete record, if record exists in file
219 if ( client.getAccountNumber() != 0 )
220 {
221 ClientData blankClient; // create blank record
222
223 // move file-position pointer to correct record in file
224 deleteFromFile.seekp( ( accountNumber - 1 ) *
225 sizeof( ClientData ) );
226
227 // replace existing record with blank record
228 deleteFromFile.write(
229 reinterpret_cast< const char * >( &blankClient ),
230 sizeof( ClientData ) );
231
232 cout << "Account #" << accountNumber << " deleted.
";
233 } // end if
234 else // display error if record does not exist
235 cerr << "Account #" << accountNumber << " is empty.
";
236 } // end deleteRecord
237
238 // display single record
239 void outputLine( ostream &output, const ClientData &record )
240 {
241 output << left << setw( 10 ) << record.getAccountNumber()
242 << setw( 16 ) << record.getLastName()
243 << setw( 11 ) << record.getFirstName()
244 << setw( 10 ) << setprecision( 2 ) << right << fixed
245 << showpoint << record.getBalance() << endl;
246 } // end function outputLine
247
248 // obtain account-number value from user
249 int getAccount( const char * const prompt )
250 {
251 int accountNumber;
252
253 // obtain account-number value
254 do
255 {
256 cout << prompt << " (1 - 100): ";
257 cin >> accountNumber;
258 } while ( accountNumber < 1 || accountNumber > 100 );
259
260 return accountNumber;
261 } // end function getAccount
The program has five options (Option 5 is for terminating the program). Option 1 calls function createTextFile
to store a formatted list of all the account information in a text file called print.txt
that may be printed. Function createTextFile
(lines 80–116) takes an fstream
object as an argument to be used to input data from the credit.dat
file. Function createTextFile
invokes istream
member function read
(lines 102–103) and uses the sequential-file-access techniques of Fig. 14.13 to input data from credit.dat
. Function outputLine
, discussed in Section 14.9, outputs the data to file print.txt
. Note that function createTextFile
uses istream
member function seekg
(line 98) to ensure that the file-position pointer is at the beginning of the file. After choosing Option 1, the print.txt
file contains
Account Last Name First Name Balance
29 Brown Nancy -24.54
33 Dunn Stacey 314.33
37 Barker Doug 0.00
88 Smith Dave 258.34
96 Stone Sam 34.98
Option 2 calls updateRecord
(lines 119–157) to update an account. This function updates only an existing record, so the function first determines whether the specified record is empty. Lines 129–130 read data into object client
, using istream
member function read
. Then line 133 compares the value returned by getAccountNumber
of the client
object to zero to determine whether the record contains information. If this value is zero, lines 155–156 print an error message indicating that the record is empty. If the record contains information, line 135 displays the record, using function outputLine
, line 140 inputs the transaction amount and lines 143–152 calculate the new balance and rewrite the record to the file. A typical execution for Option 2 is
Enter account to update (1 - 100): 37
37 Barker Doug 0.00
Enter charge (+) or payment (-): +87.99
37 Barker Doug 87.99
Option 3 calls function newRecord
(lines 160–202) to add a new account to the file. If the user enters an account number for an existing account, newRecord
displays an error message indicating that the account exists (lines 200–201). This function adds a new account in the same manner as the program of Fig. 14.12. A typical execution for Option 3 is
Enter new account number (1 - 100): 22
Enter lastname, firstname, balance
? Johnston Sarah 247.45
Option 4 calls function deleteRecord
(lines 205–236) to delete a record from the file. Line 208 prompts the user to enter the account number. Only an existing record may be deleted, so, if the specified account is empty, line 235 displays an error message. If the account exists, lines 221–230 reinitialize that account by copying an empty record (blankClient
) to the file. Line 232 displays a message to inform the user that the record has been deleted. A typical execution for Option 4 is
Enter account to delete (1 - 100): 29
Account #29 deleted.