14.11 实例研究:事务处理程序
下面介绍一个有实际意义的使用随机访问文件的事务处理程序。该程序维护银行的账目信息。
程序能够更新、添加和删除账号,并且能够把所有当前账号的格式化清单存储在一个用于打印的文本文件中。我们假定已经通过执行图14.11中的程序建立了文件eredit.dat,并用图14.12的程序插入了初始值。
程序有五个选项(第5个选项终止程序)。选项1调用函数textFile把所有的格式化的账号存储在文本文件print.txt中(以后可能要打印这个文件)。函数textFile取一个fstream对象作为参数,用于从eredit.dat文件输入数据。函数textFile用istream成员函数read和图14.14介绍的顺序文件访问方法从credit.dat输入数据。使用14.10节讨论的函数outputLine将数据输出到print.txt文件。注意textFile用istream成员函数seek保证文件位置指针在文件开头。选择了选项1后,文件accounts.txt中包含如下内容:
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
选项2调用函数updateRecord更新账号。该函数只更新已存在的记录,所以函数首先检查用户指定的记录是否为空。用istream成员函数read把记录读到结构client中,然后把成员clientaccountNumber与0比较。如果client.accountNumber为0,说明该条记录中不包含信息,因此打印出说明该记录为空的消息,然后再显示出选项菜单。如果记录中包含信息,函数utxlateR~rd用函数outputLine在屏上显示记录,并输入事务金额、计算新的结算结果以及把记录重写到文件中。选项2的典型输出如下所示.
Enter account to update (1 - 100): 37
37 Barker Doug 0.00
Enter charge (+) or payment (-): +87.99
选项3调用函数newRecord把新的账号添加到文件中。如果用户键人了一个已有的账号,函数newReeord显示出说明该账号已存在的消息,并再次显示出选项菜单。函数添加新记录的过程与图14.12中的程序所用的方法相同。选项3的典型输出如下所示:
Enter new account number(1 - 100): 22
Enter lastname, firstname, balance
? Johnston Sarah 247.45
选项4调用函数deleteRecord删除文件中的一条记录。提示用户输入账号,只能删除已存在的记录,如果该账号的记录为空,函数显示出账号不存在的错误消息。如果存在该账号,通过将空记录(blankClient)复制到文件中重新初始化该记录。删除记录时会显示一个消息。选项4的典型输出如下所示:
Enter account to delete(1 - 100): 29
Account #29 deleted.
打开 credit dat 文件时,要用 ios:: 和 ios::out 的或操作生成 fstream 对象以便读写。
1 // Fig. 14.15: figl4_15.cpp
2 // This program reads a random access file sequentially,
3 // updates data already written to the file, creates new
4 // data to be placed in the file, and deletes data
5 // already in the file.
6 #include <iostream.h>
7 #include <fstream.b>
8 #include <iomanip.h>
9 #include <stdlib.h>
10 #include "clntdata.h"
11
12 int enterChoice();
13 void textFile( fstream& );
14 void updateRecord( fstream& );
15 void newRecord( fstream& );
16 void deleteRecord( fstream& );
17 void outputLine( ostream&, const clientData & );
18 int getAccount( const char * );
19
20 enum Choices { TEXTFILE = 1, UPDATE, NEW, DELETE, END } ;
21
22 int main()
23 {
24 fstream inOutCredit( "credit.dat", ios::in | ios::out );
25
26 if ( !inOutCredit ) {
27 cerr << "File could not be opened." << endl;
28 exit ( 1 );
29 }
3O
31 int choice;
32
33 while ( ( choice = enetrChoice() ) != END){
34
35 switch ( choice ) {
36 case TEXTFILE:
37 textFile( inOutCredit );
38 break;
39 case UPDATE:
40 updateReeord( inOutCredit );
41 break;
42 case NEW:
43 newRecord( inOutCredit );
44 break;
45 case DELETE:
46 deleteRecord( inOutCredit );
47 break;
48 default:
49 cerr << "Incorrect cholce\n";
50 break;
51 }
52
53 inOutCredit.clear(); // resets end-of-file indicator
54 }
55
56 return 0;
57 }
58
59 // Prompt for and input menu choice
60 int enterChoice()
61 {
62 cout << "\nEnter your choice" << endl
63 << "i - store a formatted text file of accounts\n"
64 << " called \"print.txt\" for printing\n"
65 << "2 - update an account\n"
66 << "3 - add a new account\n"
67 << "4 - delete an account\n"
68 << "5 - end program\n? ";
69
70 int menuChoice;
71 cin >> menuChoice;
72 return menuChoice;
73 }
74
75 // Create formatted text file for printing
76 void textFile( fstream &readFromFile )
77 {
78 ofstream outPrintFile( "print.txt", ios::out );
79
80 if ( !outPrintFile ) {
81 cerr << "File could not be opened." << endl;
82 exit( 1 );
83 }
84
85 outPrintFile << setiosflags( ios::left ) << setw( 10 )
86 << "Account" << setw( 16 ) << "Last Name" << setw( 11 )
87 << "First Name" << resetiosflags( ios::left )
88 << setw( 10 ) << "Balance" << endl;
89 readFromFile.seekg(0);
90
91 clientData client;
92 readFromFile.read( reinterpret_cast<char *>( &client ),
93 sizeof( clientData ) );
94
95 while (!readFromFile.eof() ) {
96 if ( client.accountNumber != O )
97 outputLine( outPrintFile, client );
98
99 readFromFile.read( reinterpret_cast<char *>( &client ),
100 sizeof( clientData ) );
1O1 }
102 }
103
104 // Update an account's balance
105 void updateRecord( fstream &updateFile )
106 {
107 int account = getAccount( "Enter account to update" );
108
109 updateFile.seekg( ( account - I ) * sizeof( clientData ) );
110
111 clientData client;
112 updateFile.read( reinterpret_cast<char *>( &client ),
113 sizeof( clientData ) );
114
115 if ( client.accountNumber != 0 ) {
116 outputLine( cout, client );
117 cout << "\nEnter charge (+) or payment (-): ";
118
119 float transaction; // charge or payment
120 cin >> transaction; // should validate
121 client.balance += transaction;
122 outputLine( cout, client );
123 updateFile.seekp((account-1) * sizeof(clientData));
124 updateFile.write(
125 reinterpret cast<const char *>( &client ),
126 sizeof( clientData ) );
127 }
128 else
129 cerr << "Account #" << account
130 << " has no information." << endl;
131 }
132
133 // Create and insert new record
134 void newRecord( fstream &insertInFile )
135 {
136 int account = getAccount( "Enter new account number" );
137
138 insertInFile.seekg( ( account-1 ) * sizeof( clientData );
139
14O clientData client;
141 insertInFile.read( reinterpret cast<char *>( &client ),
142 sizeof( clientData ) );
143
144 if ( client.accountNumber == 0 ) {
145 cout << "Enter lastname, firstname, balance\n? ";
146 cin >> client.lastName >> client.firstName
147 >> client.balance;
148 client.accountNumber = account;
149 insertInFile.seekp( ( account - 1) *
150 sizeof( clientData ) );
151 insertInFile.write(
152 reinterpret_cast<const char *>( &client ),
153 sizeof( clientData ) );
154 }
155 else
156 cerr << "Account #" << account
157 << " already contains information." << endl;
158 }
159
160 // Delete an existing record
161 void deleteRecord( fstream &deleteFromFile )
162 {
163 int account = getAccount( "Enter account to delete" );
164
165 deleteFromFile.seekg((account-1) * sizeof( clientData ) );
166
167 clientData client;
168 deleteFromFile.read( reinterpret_cast<char *> &client ),
169 sizeof( clientData ) );
170
171 if ( client.accountNumber != 0 ) {
172 clientData blankClient = { 0, "", "", 0.0 };
173
174 deleteFromFile.seekp( ( account - 1) *
175 sizeof( clientData ) );
176 deleteFromFile.write(
177 reinterpret cast<const char *>( &blankClient ),
178 sizeof( clientData ) );
179 cout << "Account #" << account << " deleted." << endl;
180 }
181 else
182 cerr << "Account #" << account << " is empty." << endl;
183 }
184
185 // output a line of client infomation
186 void outputLine( ostream &output, const clientData &c )
187 {
188 output << setiosflags( ios::left ) << setw( 10 )
189 << c.accountNumber << setw( 16 ) << c.lastName
190 << setw( 11 } << c.firstNeme << setw( 10 )
191 << setprecision( 2 ) << resetiosflags( ios::left )
192 << setiosflags( ios::fixed | ios::showpoint )
193 << c.balance << '\n';
194 }
195
196 // Get an account number from the keyboard
197 int getAccount( const char *prompt )
198 {
199 int account;
2OO
201 do {
202 cout << prompt << " (1 - 1oo): ";
203 cin >> account;
204 } while ( account < 1 || account > 100 );
2O5
206 return account;
207 }
图 14.15 银行账目程序