14.5 读取顺序访问文件中的数据
为了在需要的时候能够检索要处理的数据,数据要存储在文件中。上一节演示丁怎样建立一个顺序访问的文件。这一节要讨论按顺序读取文件中的数据。
图14.7 中的程序读取文件"clients.dat"(图14.4中的程序建立)中的记录,并打印出了记录的内容。通过建立ifstream类对象打开文件以便输入。向对象传入的两个参数是文件名和文件打开方式。下列声明:
ifstream inClientFile( "clients.dat", ios::in );
生成ifstream对象inClientFile,并将其与打开以便输入的文件clients.dat相关联。括号中的参数传入ifstream构造函数,打开文件并建立与文件的通信线路。
打开ifstream类对象默认为进行输入,因此下列语句:
ifstream inClientFile( "Clients.dat" );
可以打开clients.dat以便输入。和ofstream对象一样,ifstream对象也可以生成而不打开特定文件,然后再将对象与文件相连接。
编程技巧 14.1
如果文件内容不能修改,那么只能打开文件以便输入(用ios::in),避免不小心改动文件。这是最低权限原则的又一个例子。
程序用!inClientFile条件确定文件是否打开成功,然后再从文件中读取数据。下列语句:
while (inClientFile >> account >> name >> balance )
从文件中读取一组值(即记录)。第一次执行完该条语句后,account的值为100,name的值为"John",balance的值为24.98。每次执行程序中的该条语句时,函数都读取文件中的另一条记录,并把新的值赋给account、name和balance。记录用函数outputLine显示,该函数用参数化流操纵算子将数据格式化之后再显示。到达文件末尾时,while结构中的输入序列返回0(通常返回inClientFile流),ifstream析构函数将文件关闭,程序终止。
1 // Fig. 14.7: fig14_O7.cpp
2 // Reading and printing a sequential file
3 #include <iostreamoh>
4 #include <fstream.h>
5 #include <iomanip.h>
6 #include <stdlib.h>
7
8 void outputLine( int, const char *, double );
9
10 int main()
11 {
12 // ifstream constructor opens the file
13 ifstream inClientFile( "clients.dat", ios::in );
14
15 if { !inClientFile ) {
16 cerr << "File could not be opened\n";
17 exit( 1 );
18 }
19
20 int account;
21 char name[ 30 ] ;
22 double balance;
23
24 cout << setiosflags( ios::left ) << setw( 10 ) << "Account"
25 << setw( 13 ) << "Name" << "Balance\n";
26
27 while ( inCiientFile >> account >> name >> balance )
28 outputLine( account, name, balance );
29
30 return 0; // ifstream destructor closes the file
31 }
32
33 void outputLine( int acct, const char *name, double bal )
34 {
35 cout << setiosflags( ios::left ) << setw( 10 ) << acct
36 << setw( 13 ) << name << setw( 7 ) << setprecision( 2 )
37 << resetiosflags( ios::left )
38 << setiosflags( ios::fixed | ios::showpoint )
39 << bal << '\n';
40 }
输出结果:
Account Name Balance
i00 Jones 24.98
200 Doe 345.67
300 White 0.00
400 Stone -42.16
500 Rich 224.62
图 14.7 读取并打印一个顺序文件
为了按顺序检索文件中的数据,程序通常要从文件的起始位置开始读取数据,然后连续地读取所有的数据,直到找到所需要的数据为止。程序执行中可能需要按顺序从文件开始位置处理文件中的数据好几次。istreatrl类和ostream类都提供成员函数,使程序把“文件位置指针”(file position pointer,指示读写操作所在的下一个字节号)重新定位。这些成员函数是istream类的seekg(“seekget”)和ostream类的seekp(“seek put”)。每个istream对象有个get指针,表示文件中下一个输入相距的字节数,每个ostream对象有一个put指针,表示文件中下一个输出相距的字节数。下列语句:
inclientFile.seekg( 0 );
将文件位置指针移到文件开头(位置0),连接inclientFile。seekg的参数通常为long类型的整数。
第二个参数可以指定寻找方向,ios::beg(默认)相对于流的开头定位,ios::cur相对于流当前位置定位,ios::end相对于流结尾定位。文件位置指针是个整数值,指定文件中离文件开头的相对位置(也称为离文件开头的偏移量)。下面是一些get文件位置指针的例子:
// position to the nth byte of fileObject
// assumes ios::beg
fileObject.seekg( n );
// position n bytes forward in fileObject
fileObject.seekg( n, ios::cur );
// position y bytes back from end of fileObject
fileObject.seekg( y, ios::end );
// position at end of fileObject
fileObject.seekg( o, ios::end );
ostream 成员函数 seekp 也可以进行类似的操作。成员函数tellg和tellp分别返回get和put指针的当前位置。下列语句将get文件位置指针值赋给long类型的变量location。
location = filObject.tellg();
图 14.8 中的程序可用来显示无借贷客户、公司债主和欠款客户的清单。公司欠债主的金额用负数表示,欠公司的金额用正数表示。程序显示了一个菜单.管理人员键入前三个选项中的一个可获取某方面的借贷信息。选项1显示出无借贷客户的清单,选项2显示公司债主的清单,选项3显示欠款客户的清单,选项4终止程序的运行。输入无效值时只是再次提示更新选择。图14. 9是程序的简单输出。
1 // Fig. 14.8; fig14_08.cpp
2 // Credit inquiry program
3 #include <iostream.b>
4 #include <fstream.h>
5 #include <iomanip.h>
6 #include <stdlib.h>
7
8 enum RequestType { ZERO_BALANCE = 1, CREDIT_BALANCE,
9 DEBIT_BALANCE, END } ;
10 int getRequest();
11 bool shouldDisplay( int, double );
12 void outputLine( int, const char *, double );
13
14 int main()
15 {
16 // ifstrem constructor opens the file
17 ifstream inClientFile( "clients.dat", ios::in );
18
19 if( !inClientFile) {
20 cerr << "File could not be opened" << endl;
21 exit( 1) ;
22 }
23
24 int request;
25 int account;
26 char name[ 30 ] ;
27 double balance;
28
29 cout << "Enter request\n"
30 << " 1 - List accounts with zero balances\n"
31 << " 2 - List accounts with credit balances\n"
32 << " 3 - List accounts with debit balances\n"
33 << " 4 - End of run";
34 request = getRquest();
35
36 while ( request != END ) {
37
38 switch ( request ) {
39 case ZERO_BALANCE:
40 cout << "\nAccounts with zero balances:\n";
41 break;
42 case CREDIT_BALANCE:
43 cout << "\nAccounts with credit balances:\n";
44 break;
45 case DEBIT_BALANCE:
46 cout <<"\nAccounts with debit balances:\n";
47 break;
48 }
49
50 inClientFile >> account >> name >> balance;
51
52 while ( !inClientFile.eof() ) {
53 if ( shouldDisplay( request, balance ) )
54 outputLine( account, name, balance );
55
56 inClientFile >> account >> name >> balance;
57 }
58
59 inClientFile.clear(); // reset eof for next input
60 inClientFile.seekg( 0 ); // move to beginning of file
61 request = getRequest();
62 }
63
64 cout << "End of run." << endl;
65
66 return 0; // ifstream destructor closes the file
67 }
68
69 int getRequest()
7O {
71 int request;
73 do {
74 cout << "\n? ";
75 cin >> request;
76 } while( request < ZERO BALANCE && request > END );
77
78 return request;
79 }
8O
81 bool shouldDisplay( int type, double balance )
82 {
83 if ( type == CREDIT_BALANCE && balance < 0 )
84 return true;
86 if ( type == DEBIT_BALANCE && balance > 0 )
87 return true;
89 if ( type == ZERO BALANCE && balance == 0 )
90 return true;
92 return false;
93 }
95 void outputLine( int acct, const char *name, double bal )
96 {
97 cout << setiosflags( ios::left ) << setw( 10 ) << acct
98 << setw( 13 ) << name << setw( 7 ) << setprecision( 2 )
99 << resetiosflags( ios::left
100 << setiosflags( ios::fixed | ios::showpoint )
101 << bal << '\n';
102 }
图14. 8 借贷查询程序
Enter request
1 - List accounts with zero balances
2 - List accounts with credit balances
3 - List accounts with debit balances
4 End of run.
? 1
Accounts with zero balances:
300 white 0.00
? 2
Accounts with credit balances:
400 stone -42.16
? 3
Accounts with debit banlances:
100 jones 24.98
200 Doe 345.67
500 Rich 224.62
? 4
End of run
图 149 图14. 8 中程序的示例输出