文件的读写
文件可以分为文本文件和二进制文件,对于文本文件的读写比较简单,使用提取运算符“>>”和插入运算符“<<”即可,而对于二进制文件的读写较为复杂一些,下面我们分别来学习。
1、文本文件的读写
文件流类fstream、ifstream和ofstream类并未直接定义文件操作的成员函数,对文件的操作通过调用其基类ios、istream和ostream的成员函数来实现,这样对文件流对象的基本操作与标准输入输出流的基本操作完全相同,即可以通过提取运算符“>>”和插入运算符“<<”来实现文件的读写。
接下来我们分别对例6-8中的a.txt文件进行读写,具体代码如例1和例2所示。
例1
1 #include <iostream>
2 #include <fstream>
3 using namespace std;
4 int main()
5 {
6 char buf[256];
7 ifstream ifs("C:\\Users\\www\\Desktop\\a.txt");
8 cout << "a.txt 内容如下:" << endl;
9 while (!ifs.eof()) //判断文件是否达到结尾
10 {
11 ifs.getline(buf, 256, '\n'); //调用基类的getline()成员函数
12 cout << buf << endl;
13 }
14 ifs.close();
15 system("pause");
16 return 0;
17 }
运行结果如图1所示。
图1 例1运行结果
在例1中,循环读取文件中的内容到buf中,在读取时直接调用了ifstream类的基类成员函数getline()来完成文件的读取。
读取完文件后,我们向文件中写入一些内容,如例2所示。
例2
1 #include <iostream>
2 #include <fstream>
3 using namespace std;
4 int main()
5 {
6 ofstream ofs;
7 //打开文件,trunc是打开文件前将文件清空
8 ofs.open("C:\\Users\\www\\Desktop\\a.txt", ios::trunc);
9 char ch = 'a';
10 if (ofs) //判断文件是否打开成功
11 {
12 for (int i = 1; i <= 26; i++)
13 {
14 if (i % 5 == 0) //到第5个字符时换行
15 ofs << endl;
16 ofs << ch; //直接调用<<运算符进行写入
17 ch++;
18 }
19 cout << "write success" << endl;
20 }
21 else
22 cout << "write failed" << endl;
23 ofs.close();
24 system("pause");
25 return 0;
26 }
运行结果如图2所示。
图2 例2运行结果
在本案例中,直接调用“<<”运算符将26个小写英文字母写入a.txt文件中,每4个字母一行。由图6-13所知,内容写入成功,则写入成功后的a.txt文件如图3所示。
图3 a.txt文件
打开模式ios::trunc在打开文件之前将文件内容清空,如图6-14所示,a.txt文件中原来的数据被清空,然后被写入了26个小写字母。
多学一招:错误处理函数
在例1中使用到了eof()函数来检测文件结尾,eof()是文件流中的错误处理函数,它遇到文件结尾会返回true(一个非零值)。除了eof()外,C++文件流还提供了其他一些进行错误处理的成员函数,它们均返回bool类型的值。
1、bad()函数
bad()函数用于检测在读写过程中是否出错,其函数声明如下所示:
bool bad();
如果在文件读写过程中出错,则返回true。例如,当我们要对一个不是打开为写状态的文件进行写入,或者我们要写入的设备没有剩余空间时,调用本函数会返回true。
2、fail()函数
fail()函数也是检错函数,其函数声明如下所示:
bool fail();
与bad()函数相同,在读写过程中出错它会返回true,除此之外,如果出现一个不可恢复的错误或一个预期的条件没有达到,例如想要读入一个整数却获得了一个字母时,它也会返回true。
3、good()函数
good()是一个通用的函数,其函数声明如下所示:
bool good();
对于good()函数来说,如果没有错误条件,则返回一个true。对上述bad()、fail()函数来说,如果返回true,则good()返回false。
4、clear()函数
clear()函数用于清除错误状态,其函数声明如下所示:
void clear(参数列表);
如果用缺少参数调用,则清除所有错误位。
5、rdstate()函数
rdstate()函数声明如下所示:
iostate rdstate()const;
rdstate()函数是返回当前错误状态。
6、二进制文件的读写
在二进制文件中,使用“<<”和“>>”运算符及函数(如getline())来输入输出数据没有什么实际意义,虽然它们都符合语法。对二进制文件的读写,文件流特殊设计了两个成员函数read()和write()。read()函数是类istream的一个成员函数,被ifstream所继承;write()函数是ostream类的一个成员函数,被ofstream类所继承;类fstream的对象同时拥有这两个函数。这两个函数的声明如下所示:
istream& read(char* buf, int size);
ostream& write(const char* buf, int size);
buf是一个内存地址,用来存储或读出数据,参数size是一个整数值,表示数据传输的字符个数。
接下来我们往一个二进制文件中写入数据然后再读取出来,以使读者熟练掌握二进制文件的读写,具体代码如例3所示。
例3
1 #include <iostream>
2 #include <fstream>
3 using namespace std;
4 struct Student
5 {
6 char name[20];
7 int age;
8 char sex;
9 };
10 struct Date
11 {
12 int mondy, day, year;
13 };
14 int main()
15 {
16 //写文件
17 Student sts[3];
18 cout << "please input 3 students' data:" << endl;
19 for (int i = 0; i < 3; i++)
20 {
21 cin >> sts[i].name>>sts[i].age>>sts[i].sex;
22 }
23 ofstream ofs("C:\\Users\\www\\Desktop\\a.dat", ios::out | ios_base::binary);
24 if (!ofs)
25 {
26 cerr << "open failed" << endl;
27 }
28
29 for (int i = 0; i < 3; i++)
30 {
31 ofs.write(reinterpret_cast<char*>(&sts[i]), sizeof(sts[i]));
32 ofs.flush();
33 }
34 ofs.close();
35 cout << "write over!" << endl << "read from a.dat:" << endl;
36 //读文件
37 ifstream ifs("C:\\Users\\www\\Desktop\\a.dat", ios::in | ios_base::binary);
38 if (ifs)
39 {
40 Student sts1[3];
41 for (int i = 0; i < 3; i++)
42 {
43 ifs.read(reinterpret_cast<char*>(&sts1[i]), sizeof(sts1[i]));
44 cout << sts1[i].name << " " << sts1[i].age << " " << sts1[i].sex << endl;
45 }
46 }
47 else
48 cout << "ifs failed" << endl;
49 ifs.close();
50 system("pause");
51 return 0;
52 }
运行结果如图4所示。
图4 例3运行结果
例3中,首先构造一个输出流对象,调用write()函数向a.dat二进制文件中写入数据,如果无此文件则创建一个新的文件。写入数据之后,将文件关闭,再构造一个输入流对象,调用read()函数读取a.dat文件中的内容读到控制台。本案例中是将一个结构体数组中的数据写入到文件,在进行地址参数传递时调用了reinterprte_cast<>转换运算符将结构体地址显示转换成char*类型。
注意:在进行二进行文件读写时,文件打开模式必须是binary,否则文件会以ASCII形式打开文件。