写在前面:在学习文件操作时,因为跳过了一页所以没有看到cin.get() 会改变流状态所以犯了这个错误。这是我看书时两段程序的拼接,所以文件打开关闭了两次,在这里写下我对这段代码的理解以供日后参考。
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cstdlib>
const int LIM = 20;
struct planet {
char name[LIM];
double population;
double g;
};
const char* file = "planets.dat";
inline void eatline() { while (std::cin.get() != '\n')continue; }
int main() {
using namespace std;
planet pl;
cout << fixed;
//add elements
fstream ftemp(file,
ios_base::out|ios_base::binary);
if (!ftemp.is_open()) {
cerr << "Can't open"<<file<<" file for output.\n";
exit(EXIT_FAILURE);
}
cout << "Enter planet name (enter a blank line to quit): ";
cin.get(pl.name, 20);
cout << cin.rdstate()<<endl;
while (pl.name[0] != '\0') {
eatline();
cout << "Enter planetary population: ";
cin >> pl.population;
cout << "Enter planet's acceleration of gravity: ";
cin >> pl.g;
eatline();
ftemp.write((char*)&pl, sizeof pl);
cout << "Enter planet name (enter a blank line to quit): ";
cin.get(pl.name, 20);
cout << cin.rdstate()<<endl;
}
cin.clear();
ftemp.close();
//if (cin.good()) cout << "good\n";
//if (cin.eof()) cout << "eof\n";
//if (cin.bad()) cout << "bad\n";
//if (cin.fail()) cout << "fail\n";
//show contents
fstream finout;
finout.open(file,
ios_base::in | ios_base::out | ios_base::binary);
int ct = 0;
if (finout.is_open()) {
finout.seekg(0);
cout << "Here are the current contents of the "
<< file << " file:\n";
while (finout.read((char*)&pl, sizeof pl)) {
cout << ct++ << ": " << setw(LIM) << pl.name << ": "
<< setprecision(0) << setw(12) << pl.population
<< setprecision(2) << setw(6) << pl.g << endl;
}
if (finout.eof()) finout.clear();
else {
cerr << "Error in reading " << file << ".\n";
exit(EXIT_FAILURE);
}
}
else {
cerr << file << " could not be opened -- bye.\n";
exit(EXIT_FAILURE);
}
//change a record
cout << "Enter the record number you wish to change: ";
long rec = 0;
//eatline();
//getchar();
cin >> rec;
eatline();
if (rec < 0 || rec >= ct) {
cerr << "Invalid record number -- bye\n";
exit(EXIT_FAILURE);
}
streampos place = rec * sizeof pl;
finout.seekg(place);
if (finout.fail()) {
cerr << "Error on attempted seek\n";
exit(EXIT_FAILURE);
}
finout.read((char*)&pl, sizeof pl);
cout << "Your selection:\n";
cout<<rec<<": "<<setw(LIM)<<pl.name<<": "
<< setprecision(0) << setw(12) << pl.population
<< setprecision(2) << setw(6) << pl.g << endl;
if (finout.eof()) finout.clear();
cout << "Enter planet name: ";
cin.get(pl.name, LIM);
eatline();
cout << "Enter planetary population: ";
cin >> pl.population;
cout << "Enter planet's acceleration of gravity: ";
cin >> pl.g;
finout.seekp(place);
finout.write((char*)&pl, sizeof pl);
if (finout.fail()) {
cerr << "Error on attempted write\n";
exit(EXIT_FAILURE);
}
//show revised file
ct = 0;
finout.seekg(0);
cout << "Here are the new contents of the "
<< file << " file:\n";
while (finout.read((char*)&pl, sizeof pl)) {
cout << ct++ << ": " << setw(LIM) << pl.name << ": "
<< setprecision(0) << setw(12) << pl.population
<< setprecision(2) << setw(6) << pl.g << endl;
}
finout.close();
cout << "Done.\n";
return 0;
}
运行过程:
1.创建二进制文件 planets.dat 并打开,进行写入操作(如果已有该文件将清楚文件内容重新写入)。这里我添加了一句输出 cin 的流状态,可在每次输入 name 检查流状态,因为一开始我没有注意流状态所以文件只能进行到第一次写入而不能继续接下来的 cin 操作。
2.执行完第一段后就会发现,cin 的流状态变为了 failbit ,导致不能继续读入数据。流状态变为 failbit 的原因是因为 cin.get() 函数读取了一个空行(这里程序以接收空行为结束标志)。因此需要重置流状态,调用一次 cin.clear() 函数即可。
3.再次打开文件,以读写二进制文件的形式。如果打开文件则将文件的读指针移到文件开头,从开头读到文件结尾即读出文件所有信息,此时流状态被设为 eofbit ,再对流状态进行重置,同上使用clear() 函数。
4.进行更改信息操作。读入信息标号从而确定选定的信息,将该信息输出后进行写入覆盖原有的数据,完成更新。
5.最后将文件信息完整输出一遍。
运行示例: