第43节 文件
1、文件的概念
►1. 流
►C++的输入输出是以字节流的形式实现的。
►流是指由若干字节组成的字节序列的数据从一个对象传递到另一个对象的操作。
►从流中读取数据称为提取操作,向流内添加数据称为插入操作。
►流在使用前要建立,使用后要删除。
►流具有方向性:与输入设备相联系的流称为输入流,与输出设备相联系的流称为输出流,与输入输出设备相联
系的流称为输入输出流。
►C++输入输出类库中包含许多流类(stream class),如下图所示。
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/35b9dd85f00c44728cc977ed37d3392e.png)
►提取和插入运算符
►在istream类中已经将运算符(>>)重载为提取运算符,支持基本数据类型及其指针类型。
►在ostream类中已经将运算符(<<)重载为插入运算符,其适用类型除了前述类型外,还增加了void*类型。
►预定义流对象
►标准库将常用iostream类的流对象,定义在<iostream>头文件中,它们是:
►①cin:与标准输入设备相关联的标准输入流(istream对象);
►②cout:与标准输出设备相关联的标准输出流(ostream对象);
►2. 文件
►程序常常需要将一些数据信息(如运行结果)永久性地保存下来,或者从永久性信息中读取有用的数据(如历
史记录),这些都需要进行文件操作。C++文件操作是通过文件流实现的。
►文件是指存放在磁盘上数据的集合。操作系统以文件为单位对这些数据进行管理。也就是说,如果想得到存在
磁盘上的数据,必须先按文件名找到指定的文件,然后再从该文件中读取数据。要向磁盘上存放数据也必须
先以文件名为标识创建建一个文件,才能向它输出数据。
►文件按数据的组织形式可以分为两类:
►(1)ASCII文本文件
►文本(text)文件对于ASCII字符集而言,文件中每个字节存放的是一个ASCII码,表示一个字符;对
于像汉字、日韩文字等字符集而言,使用双字节存放字符。
►(2)二进制文件
►二进制文件是将数据以内存中的存储形式直接存放到磁盘上。用二进制形式输出数据,可以节省存储空
间和避免编码转换。由于一个字节并不对应一个字符,所以不能直接打印输出或编辑二进制文件。
2、文件的操作
►1. 文件打开
►已创建的文件流对象需要和指定的磁盘文件建立关联,以便使文件流流向指定的磁盘文件,这个过程称为文件打开。
►打开文件有两种方式:
►(1)是定义文件流对象时使用带参数的构造函数;
►(2)是调用文件流成员函数open。
ifstream();
ifstream(const char* filename,ios_base::openmode mode=ios_base::in);
ofstream();
ofstream(const char* filename,ios_base::openmode mode=ios_base::out);
fstream();
fstream(const char* filename,ios_base::openmode mode=ios_base::in|ios_base::out);
void open(const char* filename,ios_base::openmode mode=ios_base::in);
bool operator!();
bool is_open();
►输入:读操作,将文件中的内容读到内存中。
►输出:写操作,将内存中的数据写到文件中。
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/3311a76534b14fab85381a659dacbcfb.png)
►说明:
►(1)可以用位或运算(|)对openmode进行组合。
►(2)打开文件时操作可能会失败,如果打开操作失败,open函数返回值为0(假)。如果是调用构造函
数打开文件且打开操作失败,则流对象的值为0(空对象)。可以据此测试打开是否成功,确定能
否对该文件继续操作。
►(3)每一个打开的文件都有一个文件指针,该指针初始位置要么在文件末尾(当指定ios_base::app或
ios_base::ate时),要么在文件开头。每次读写都从文件指针的当前位置开始。每读写一个字节,
指针就自动后移一个字节。当文件指针移到最后,就会遇到文件结束EOF(文件结束符)。
►2. 文件关闭
►当不再使用文件时,应该关闭该文件。关闭文件可以调用文件流成员函数:
void close();
►当文件关闭后,就不能再通过流对象对文件进行操作了,除非再次打开。
►3. 文件状态
►文件流提供如下四个成员函数用来检测文件状态:
bool eof(); bool bad(); bool fail(); bool good();
►如果文件已到末尾,eof函数返回真(1),否则返回假(0)。如果在读写文件过程中出错,bad函数返
回true。如对一个不是为写状态打开的文件进行写入,或者要写入的设备没有剩余空间。除了与bad
函数同样的情况下会返回true以外,格式错误时fail函数也会返回true。如要读入一个整数而得到
一个字母时。
►如果调用以上任何一个函数返回true的话,good函数函数返回false。
►4. 文件操作的基本形式
►几乎所有文件应用中的打开和关闭的程序形式是相同的,为此给出通用的文件打开和关闭的操作步骤:
►①定义文件流对象;
►②通过构造函数或者成员函数open打开文件(或创建文件);
►③打开文件失败时中断文件处理;
►④对文件进行各种操作;
►⑤文件处理结束时关闭文件。
►文件操作的基本形式---代码形式
ifstream infile(文件名,openmode);
if (!infile) {
…
infile.close();
}
或:
ifstream infile;
infile.open("文件名",openmode);
if (!infile.fail()) {
…
infile.close();
}
►文件操作包括读写和定位,除流提取和流插入运算符外,文件流还有如下有用的操作文件的成员函数:
istream& read(char* s,streamsize n);
streampos tellg();
istream& seekg(streampos pos);
istream& seekg(streamoff off,ios_base::seekdir dir);
ostream& write(const char* s,streamsize n);
streampos tellp();
ostream& seekp(streampos pos);
ostream& seekp(streamoff off,ios_base::seekdir dir);
flush();
►5. 文件操作举例
►(1)对ASCII文件操作。
►对ASCII文件的读写操作可以用以下两种方法:
►①用流插入(<<)运算符和流提取(>>)运算符输入输出标准类型的数据;
►②用流对象成员函数get、getline、put等进行字符的输人输出。
【例43.1】将源文件每行文本添加一个行号输出到目的文件中。
1 #include <fstream>
2 #include <iomanip>
3 using namespace std;
4 int main()
5 {
6 char s1[500]; int cnt=0;
7 ifstream inf("a.cpp");
8 if (!inf.fail()) {
9 ofstream outf("b.cpp");
10 while (!inf.eof()) {
11 inf.getline(s1,sizeof(s1)-1);
12
13 outf<<setfill('0')<<setw(4)<<++cnt<<" "<<s1<<endl;
14 }
15 outf.close();
16 inf.close();
17 }
18 return 0;
19 }
►(2)对二进制文件操作。
►二进制文件打开时要用ios_base::binary指定为以二进制形式读取和存储。
►二进制文件除了可以作为输入文件或输出文件外,还可以是既能输入又能输出的文件。
这是和ASCII文件不同的地方。
►对二进制文件的读写操作主要使用流成员函数read和write。
【例43.2】复制源文件内容到目的文件。
1 #include <iostream>
2 #include <fstream>
3 using namespace std;
4 int main()
5 {
6 char src[260],dest[260],buff[16384];
7 ifstream inf("book.dat",ios_base::in|ios_base::binary);
8 if (!inf.fail()) {
9 ofstream outf("out.dat",ios_base::out|ios_base:: binary);
10 while (!inf.eof()) {
11 inf.read(buff,sizeof(buff));
12 outf.write(buff,inf.gcount());
13 cout<<buff<<endl;
14 }
15 outf.close();
16 inf.close();
17 }
18 return 0;
19 }
►(3)随机访问二进制文件。
►一般情况下读写文件是顺序进行的,即逐个字节进行读写。但是对于二进制文件来说,可以利用seekg或
seekp成员函数移动文件指针,从而随机地访问文件中任一位置上的数据,还可以修改文件中的内容。
【例43.3】已知文件book.dat文件中有100个数据销售记录,每个销售记录由代码(char c[5])、书名
(char n[11])、单价(int p)和数量(int q)4个组成部分组成。文件每行包含代码、书名、单价、
数量,用Tab间隔,格式如下:
1001 软件世界 5 100
1002 计算机工程 6 120
……
要求将所有记录写入到out.dat文件中,然后将out.dat的第一个记录进行输出。
1 #include <fstream>
2 using namespace std;
3 struct BOOK {
4 char c[5];
5 char n[11];
6 int p;
7 int q;
8 };
9 int main()
10 {
11 BOOK a;
12 ifstream inf("book.dat");
13 ios_base::openmode m=ios_base::in|ios_base::out;
14 fstream iof("out.dat",m|ios_base::trunc|ios_base::binary);
15 if (inf.fail()||iof.fail()) return -1;
16 while(!inf.eof()) {
17 inf>>a.c>>a.n>>a.p>>a.q;
18 iof.write((char*)&a,sizeof(BOOK));
19 }
20 inf.close();
21 iof.seekg(0*sizeof(BOOK),ios_base::beg);
22 cout<<a.c<<a.n<<a.p<<a.q;
23 iof.read((char*)&a,sizeof(BOOK));
24 iof.close();
25 return 0;
26 }