ITEEDU

14.9 向随机访问文件中随机地写入数据

图14.12 中的程序把数据写到文件“credit dm”中。ostream的函数seekp和write用来将数据存储在文件中指定的位置。程序先用函数seekp把“put”文件位置指针指向文件中指定的位置,然后用write函数写入数据。执行范例见图14. 13。注意图14. 12的程序包括图14.11中定义的头文件clntdata h。

// Fig. 14.12: figl4_12.cpp
// Writing to a random access file
#include <iostream.h>
#include <fstream.h>
#include <stdlib.h>
#include "clntdata.h"
int main()
{
ofstream outCredit( "credit.dat", ios::ate );
if ( !outCredit ) {
cerr << "File could not be opened." << endl;
exit( 1);
}
cout << "Enter account number "
<< "(1 to 100, 0 to end input)\n? ";
clientData client;
cin >> client.accountNumber;
while ( client.accountNumber > 0 &&
client.accountNumber <= 100 ) {
cout << "Enter lastname, firstname, balance\n? ";
cin >> client.lastName>> client.firstName
>> client.balance;
outCredit.seekp( ( client.accountNumber - 1 ) *
sizeof( clientData ) );
outCredit.write(
reinterpret_cast<const char *>( &client ),
sizeof( clientData ) );
cout << "Enter account number\n? ";
cin >> client.accountNumber;
}
return 0;
}
图14. 12 把数据随机地写人随机访问文件中

Enter account number (1 to 100, 0 to end input)
? 37
Enter lastname,firstname,balance
? Barker Doug 0.00
Enter account number
? 29
Enter lastname, firstname, balance
? Barker Nancy -24.54
Enter account number
? 96
Enter lastname, firstname, balance
? Stone Sam 34.98
Enter account number
? 88
Enter lastname, firstname, balance
? Smith Dave 258.34
Enter account number
? 33
Enter lastname, firstname, balance
? Dunn Stacey 314.33
Enter account number
?0
图14.13 图14.12中程序的示例输出

第29行和第30行:

outCredit.seekp( ( client.accountNumber - 1 ) *
sizeof( ciientData ) );

将outCredit对象的“put”文件位置指针放在(client.accountNumber-1)*sizof(clientData)求出的字节位置处。由于账号在1到100之间.因此计算记录的字节位置时要从账号减1。这样,对于记录1,文件位置指针设置为文件的字节0。注意ofstream的对象outCredit用文件打开方式ios::ate打开。“put”文件位置指针最初设置为文件末尾,但数据可以在文件中的任何地方写入。

14.10 从随机访问文件中顺序地读取数据

上面几节生成了随机访问文件并将数据写入这个文件中。本节要开发一个程序,顺序读取这个文件.只打印包含数据的记录。该程序还有另一好处,将在本节最后说明,读者不妨先猜猜看。

istream的函数read从指定流的当前位置向对象输入指定字节数。例如,图14.14中下列语句:

inCredit.read(reinterpret_cast<char *)(&client),
sizeof(clientData));

从与ifstrem的对象inCredit相关联的文件中读取sizeof(clientData)指定的字节数,并将数据存放在结构client中。注意函数read要求第一个参数类型为char*。由于&client的类型为clientData *,因此&client要用强制类型转换运算符interpret_cast变为char*。注意图14.14中的程序中包括图14.11定义的头文件clntdata. h。

 // Fig. 14.14: figl4_14.cpp
 // Reading a random access file sequentially
 #include <iostream.h>
#include <iomanip.h>
#include <fstream.h>
#include <stdlib.h> #include "clntdata.h" void outputLine( ostream&, const clientData & ); int main() { ifstream inCredit( "credit.dat", ios::in ); if ( !inCredit ) { cerr << "File could not be opened." << endl; exit( 1 ); } cout << setiosflags( ios::left ) << setw( 10 ) << "Account" << setw( 16 ) << "Last Name" << setw( 11 ) << "first Name" << resetiosflags( ios::left ) << setw( 10 ) << "Balance" << endl; clientData client; inCredit.read( reinterpret_cast ( &client ), sizeof( clientData ) ); while ( inCredit && !inCredit.eof() ) { if { client.accountNumber != 0 ) outputLine( cout, client ); inCredit.read( reinterpret_cast( &client ), sizeof( clientData ) ); } return 0; } void outputLine( ostream &output, const clientData &c ) { output << setiosflags( ios::left ) << setw( 10 ) << c.accountNumber << setw( 16 ) << c.lastName << setw( 11 ) << c.firstName << setw( 10 ) << setprecision( 2 ) << resetiosflags( ios::left ) << setiosflags( ios::fixed | ios::showpoint ) << c.balance << '\n'; }
输出结果:
Account    Last Name   First Name    Balance
29         Brown       Nancy         -24.54
33         Dunn        Stacey        319.33
37         Barker      Doug            9.00
88         Smith       Dave          258.34
96         Stone       Sam            34.98
图14.14 从随机访问文件中顺序地读取数据

图14.14 的程序顺序读取"credit.dat"文件中的每个记录,检查每个记录中是否包含数据,并打印包含数据的记录。第30行的下列条件:

while(inCredit && !inCredit.eof(){

用ios成员函数eof确定是否到达文件末尾,如果到达文件末尾,则终止执行while结构。如果读取文件时发生错误,则循环终止,因为inCredit值为false。从文件中输入的数据用outputLine输出,outputLine取两个参数,即ostream对象和要输出的clientData结构。osttream参数类型可以支持任何ostream对象(如cont)或ostream的任何派生类对象(如ofstream类型的对象)作为参数。这样,同一函数既可输出到标准输出流,也可输出到文件流,而不必编写另一个函数。

这些程序还有另一好处,输出窗口中的记录已经按账号排序列出,这是用直接访问方法将这些记录存放到文件中的结果。试比较第4章介绍的冒泡排序,用直接访问方法排序显然快多了。这个速度是通过生成足够大的文件来保证完成每个记录而实现的。当然,大多数时候文件存储都是稀松的,因此会浪费存储空间。这是另一个以空间换取时间的例子:加大空间可以得到更快的排序算法。