www.gibmonks.com

Main Page




Previous Page
Next Page

[Page 867 (continued)]

17.11. Case Study: A Transaction-Processing Program

We now present a substantial transaction-processing program (Fig. 17.15) using a random-access file to achieve "instant" access processing. The program maintains a bank's account information. The program 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. 17.12 has been executed to create the file credit.dat and that the program of Fig. 17.13 has been executed to insert the initial data.

Figure 17.15. Bank account program.
(This item is displayed on pages 867 - 872 in the print version)

 1   // Fig. 17.15: Fig17_15.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 in the file.
 5   #include <iostream>
 6   using std::cerr;
 7   using std::cin;
 8   using std::cout;
 9   using std::endl;
10   using std::fixed;
11   using std::ios;
12   using std::left;
13   using std::right;
14   using std::showpoint;
15 
16   #include <fstream>  
17   using std::ofstream;
18   using std::ostream; 
19   using std::fstream; 
20 
21   #include <iomanip>
22   using std::setw;
23   using std::setprecision;
24 
25   #include <cstdlib>
26   using std::exit; // exit function prototype
27 
28   #include "ClientData.h" // ClientData class definition
29 
30   int enterChoice();
31   void createTextFile( fstream& );
32   void updateRecord( fstream& );
33   void newRecord( fstream& );
34   void deleteRecord( fstream& );
35   void outputLine( ostream&, const ClientData & );
36   int getAccount( const char * const );
37 
38   enum Choices { PRINT = 1, UPDATE, NEW, DELETE, END };
39 
40   int main()
41   {
42      // open file for reading and writing                    
43      fstream inOutCredit( "credit.dat", ios::in | ios::out );
44 
45      // exit program if fstream cannot open file
46      if ( !inOutCredit )
47      {
48         cerr << "File could not be opened." << endl;
49         exit ( 1 );
50      } // end if
51 
52      int choice; // store user choice
53 
54      // enable user to specify action
55      while ( ( choice = enterChoice() ) != END )
56      {
57         switch ( choice )
58         {
59            case PRINT: // create text file from record file
60               createTextFile( inOutCredit );
61               break;
62            case UPDATE: // update record
63               updateRecord( inOutCredit );
64               break;
65            case NEW: // create record
66               newRecord( inOutCredit );
67               break;
68            case DELETE: // delete existing record
69               deleteRecord( inOutCredit );
70               break;
71            default: // display error if user does not select valid choice
72               cerr << "Incorrect choice" << endl;
73               break;
74         } // end switch
75 
76         inOutCredit.clear(); // reset end-of-file indicator
77      } // end while
78 
79      return 0;
80   } // end main
81 
82   // enable user to input menu choice
83   int enterChoice()
84   {
85      // display available options
86      cout << "\nEnter your choice" << endl
87         << "1 - store a formatted text file of accounts" << endl
88         << "    called \"print.txt\" for printing" << endl
89         << "2 - update an account" << endl
90         << "3 - add a new account" << endl
91         << "4 - delete an account" << endl
92         << "5 - end program\n? ";
93 
94      int menuChoice;
95      cin >> menuChoice; // input menu selection from user
96      return menuChoice;
97   } // end function enterChoice
98 
99   // create formatted text file for printing
100  void createTextFile( fstream &readFromFile )
101  {
102     // create text file                            
103     ofstream outPrintFile( "print.txt", ios::out );
104
105     // exit program if ofstream cannot create file
106     if ( !outPrintFile )
107     {
108        cerr << "File could not be created." << endl;
109        exit( 1 );
110     } // end if
111
112     outPrintFile << left << setw( 10 ) << "Account" << setw( 16 )
113        << "Last Name" << setw( 11 ) << "First Name" << right     
114        << setw( 10 ) << "Balance" << endl;                       
115
116     // set file-position pointer to beginning of readFromFile
117     readFromFile.seekg( 0 );                                 
118
119     // read first record from record file
120     ClientData client;
121     readFromFile.read( reinterpret_cast< char * >( &client ),
122        sizeof( ClientData ) );                               
123
124     // copy all records from record file into text file
125     while ( !readFromFile.eof() )
126     {
127        // write single record to text file
128        if ( client.getAccountNumber() != 0 )  // skip empty records
129           outputLine( outPrintFile, client );
130
131        // read next record from record file                     
132        readFromFile.read( reinterpret_cast< char * >( &client ),
133           sizeof( ClientData ) );                               
134     } // end while
135  } // end function createTextFile
136
137  // update balance in record
138  void updateRecord( fstream &updateFile )
139  {
140     // obtain number of account to update
141     int accountNumber = getAccount( "Enter account to update" );
142
143     // move file-position pointer to correct record in file          
144     updateFile.seekg( ( accountNumber - 1 ) * sizeof( ClientData ) );
145
146     // read first record from file
147     ClientData client;
148     updateFile.read( reinterpret_cast< char * >( &client ),
149        sizeof( ClientData ) );                             
150
151     // update record
152     if ( client.getAccountNumber() != 0 )
153     {
154        outputLine( cout, client ); // display the record
155
156        // request user to specify transaction
157        cout << "\nEnter charge (+) or payment (-): ";
158        double transaction; // charge or payment
159        cin >> transaction;
160
161        // update record balance
162        double oldBalance = client.getBalance();
163        client.setBalance( oldBalance + transaction );
164        outputLine( cout, client ); // display the record
165
166        // move file-position pointer to correct record in file          
167        updateFile.seekp( ( accountNumber - 1 ) * sizeof( ClientData ) );
168
169        // write updated record over old record in file               
170        updateFile.write( reinterpret_cast< const char * >( &client ),
171           sizeof( ClientData ) );                                    
172     } // end if
173     else // display error if account does not exist
174        cerr << "Account #" << accountNumber
175           << " has no information." << endl;
176  } // end function updateRecord
177
178  // create and insert record
179  void newRecord( fstream &insertInFile )
180  {
181     // obtain number of account to create
182     int accountNumber = getAccount( "Enter new account number" );
183
184     // move file-position pointer to correct record in file
185     insertInFile.seekg( ( accountNumber - 1 ) * sizeof( ClientData ) );
186
187     // read record from file
188     ClientData client;
189     insertInFile.read( reinterpret_cast< char * >( &client ),
190        sizeof( ClientData ) );                               
191
192     // create record, if record does not previously exist
193     if ( client.getAccountNumber() == 0 )
194     {
195        char lastName[ 15 ];
196        char firstName[ 10 ];
197        double balance;
198
199        // user enters last name, first name and balance
200        cout << "Enter lastname, firstname, balance\n? ";
201        cin >> setw( 15 ) >> lastName;
202        cin >> setw( 10 ) >> firstName;
203        cin >> balance;
204
205        // use values to populate account values
206        client.setLastName( lastName );
207        client.setFirstName( firstName );
208        client.setBalance( balance );
209        client.setAccountNumber( accountNumber );
210
211        // move file-position pointer to correct record in file            
212        insertInFile.seekp( ( accountNumber - 1 ) * sizeof( ClientData ) );
213
214        // insert record in file                                        
215        insertInFile.write( reinterpret_cast< const char * >( &client ),
216           sizeof( ClientData ) );                                      
217     } // end if
218     else // display error if account already exists
219        cerr << "Account #" << accountNumber
220           << " already contains information." << endl;
221  } // end function newRecord
222
223  // delete an existing record
224  void deleteRecord( fstream &deleteFromFile )
225  {
226     // obtain number of account to delete
227     int accountNumber = getAccount( "Enter account to delete" );
228
229     // move file-position pointer to correct record in file             
230     deleteFromFile.seekg( ( accountNumber - 1 ) * sizeof(ClientData ) );
231
232     // read record from file
233     ClientData client;
234     deleteFromFile.read( reinterpret_cast< char * >( &client ),
235        sizeof( ClientData ) );                                 
236
237     // delete record, if record exists in file
238     if ( client.getAccountNumber() != 0 )
239     {
240        ClientData blankClient; // create blank record
241
242        // move file-position pointer to correct record in file
243        deleteFromFile.seekp( ( accountNumber - 1 ) *          
244           sizeof( ClientData ) );                             
245
246        // replace existing record with blank record        
247        deleteFromFile.write(                               
248           reinterpret_cast< const char * >( &blankClient ),
249           sizeof( ClientData ) );                          
250
251        cout << "Account #" << accountNumber << " deleted.\n";
252     } // end if
253     else // display error if record does not exist
254        cerr << "Account #" << accountNumber << " is empty.\n";
255  } // end deleteRecord
256
257  // display single record
258  void outputLine( ostream &output, const ClientData &record )
259  {
260     output << left << setw( 10 ) << record.getAccountNumber()
261        << setw( 16 ) << record.getLastName()
262        << setw( 11 ) << record.getFirstName()
263        << setw( 10 ) << setprecision( 2 ) << right << fixed
264        << showpoint << record.getBalance() << endl;
265  } // end function outputLine
266
267  // obtain account-number value from user
268  int getAccount( const char * const prompt )
269  {
270     int accountNumber;
271
272     // obtain account-number value
273     do
274     {
275        cout << prompt << " (1 - 100): ";
276        cin >> accountNumber;
277     } while ( accountNumber < 1 || accountNumber > 100 );
278
279     return accountNumber;
280  } // end function getAccount


[Page 872]

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 100135) 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 132133) and uses the sequential-file-access techniques of Fig. 17.14 to input data from credit.dat. Function outputLine, discussed in Section 17.10, is used to output the data to file print.txt. Note that createTextFile uses istream member function seekg (line 117) to ensure that the file-position pointer is at the beginning of the file. After choosing Option 1, the print.txt file contains


[Page 873]

 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 138176) to update an account. This function updates only an existing record, so the function first determines whether the specified record is empty. Lines 148149 read data into object client, using istream member function read. Then line 152 compares the value returned by getAccountNumber of the client structure to zero to determine whether the record contains information. If this value is zero, lines 174175 print an error message indicating that the record is empty. If the record contains information, line 154 displays the record, using function outputLine, line 159 inputs the transaction amount and lines 162171 calculate the new balance and rewrite the record to the file. A typical output 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 179221) 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 219220). This function adds a new account in the same manner as the program of Fig. 17.12. A typical output 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 224255) to delete a record from the file. Line 227 prompts the user to enter the account number. Only an existing record may be deleted, so, if the specified account is empty, line 254 displays an error message. If the account exists, lines 247249 reinitialize that account by copying an empty record (blankClient) to the file. Line 251 displays a message to inform the user that the record has been deleted. A typical output for Option 4 is

 Enter account to delete (1 - 100): 29
 Account #29 deleted.


Note that line 43 opens the credit.dat file by creating an fstream object for both reading and writing, using modes ios::in and ios::out "or-ed" together.


Previous Page
Next Page