本文主要介绍C++ 中文件操作的相关知识。
概述
C++ 为字符,字符串,文件提供了一套输入输出的操作机制,称之为流。下图为 C++ 中输入输出流的关系图。
对于文件操作,我们需要用到<fstream>
,与之相关的流有ifstream,ofstream, fstream, filebuf
。下面对这四种流进行介绍。
1 | #include <fstream> |
C++ 文件操作的基本顺序:
- 打开文件
- 读写操作文件
- 关闭文件
打开文件
在fstream类中,使用成员函数open()实现打开文件的操作,从而将数据流和文件进行关联,通过ofstream, ifstream, fstream, filebuf 对象进行对文件的读写操作。
fstream::open()函数形式:
1 | /** |
文件打开方式
打开方式 | 说明 |
---|---|
ios::in | 为读文件而打开 |
ios:out | 为写文件而打开 |
ios::ate | 初始位置为文件尾 |
ios::app | 所有输出附加在文件尾 |
ios::trunc | 如果文件已存在则先删除该文件 |
ios::binary | 二进制方式打开文件 |
说明:
文件打开方式可以组合使用,通过|
隔开。
其他相关函数
fstream::is_open()
: 检查文件是否打开filebuf::open()
: 打开文件与 file stream buffer object关联
操作文件
读写文件分为文本文件和二进制文件的读取,对于文本文件的读取比较简单,用插入器和析取器就可以了;而对于二进制的读取就要复杂些。
文本文件的读写
文本文件的读写很简单:用插入器<<
向文件输出;用析取器>>
从文件输入。假设file1是以输入方式打开,file2以输出打开。示例如下:
1 | file2<<"I Love You";//向文件写入字符串"I Love You" |
示例:
1 | // writing on a text file |
从文件中读入数据也可以用与 cin>>的使用同样的方法:
1 | // reading a text file |
这种方式还有一种简单的格式化能力,比如可以指定输出为16进制等等,具体的格式有以下一些
操纵符 | 功能 | 输入/输出 |
---|---|---|
dec | 格式化为十进制数值数据 | 输入和输出 |
endl | 输出一个换行符并刷新此流 | 输出 |
ends | 输出一个空字符 | 输出 |
hex | 格式化为十六进制数值数据 | 输入和输出 |
oct | 格式化为八进制数值数据 | 输入和输出 |
setpxecision(int p) | 设置浮点数的精度位数 | 输出 |
示例:
1 | //将123当作十六进制输出: |
二进制文件的读写
put()
函数
put()函数向流写入一个字符,其原型是ofstream &put(char ch),使用也比较简单,如file1.put(‘c’);就是向流写一个字符’c’。get()
函数
get()函数比较灵活,有3种常用的重载形式:ifstream &get(char &ch);
功能:从流中读取一个字符,结果保存在引用ch中,如果到文件尾,返回空字符。如file2.get(x);
表示从文件中读取一个字符,并把读取的字符保存在x中。int get();
这种形式是从流中返回一个字符,如果到达文件尾,返回EOF,如x=file2.get();
和上例功能是一样的。ifstream &get(char *buf,int num,char delim='n');
这种形式把字符读入由 buf 指向的数组,直到读入了 num 个字符或遇到了由 delim 指定的字符,如果没使用 delim 这个参数,将使用缺省值换行符’n’。例如:file2.get(str1,127,'A');
从文件中读取字符到字符串str1,当遇到字符’A’或读取了127个字符时终止。
读写数据块
要读写二进制数据块,使用成员函数read()和write()成员函数,它们原型如下:1
2read(unsigned char *buf,int num);
write(const unsigned char *buf,int num);read()
函数
从文件中读取 num 个字符到 buf 指向的缓存中,如果在还未读入 num 个字符时就到了文件尾,可以用成员函数 int gcount();来取得实际读取的字符数;write()
函数
从buf 指向的缓存写 num 个字符到文件中,值得注意的是缓存的类型是unsigned char *
,有时可能需要类型转换。
示例:1
2
3
4
5
6
7unsigned char str1[]="I Love You";
int n[5];
ifstream in("xxx.xxx");
ofstream out("yyy.yyy");
out.write(str1,strlen(str1));//把字符串str1全部写到yyy.yyy中
in.read((unsigned char*)n,sizeof(n));//从xxx.xxx中读取指定个整数,注意类型转换
in.close();out.close();
示例:
二进制文件读写
1 | // reading binary file |
获取二进制文件大小
1 | // obtaining file size |
检测EOF
成员函数eof()用来检测是否到达文件尾,如果到达文件尾返回非0值,否则返回0。原型是int eof();
示例:
1 | if(in.eof())ShowMessage("已经到达文件尾!"); |
除了eof()
外,还有一些验证流的状态的成员函数(所有都返回bool型返回值):
bad()
如果在读写过程中出错,返回 true 。例如:当我们要对一个不是打开为写状态的文件进行写入时,或者我们要写入的设备没有剩余空间的时候。fail()
除了与bad() 同样的情况下会返回 true 以外,加上格式错误时也返回true ,例如当想要读入一个整数,而获得了一个字母的时候。eof()
如果读文件到达文件末尾,返回true。good()
这是最通用的:如果调用以上任何一个函数返回true 的话,此函数返回 false 。clear()
要想重置以上成员函数所检查的状态标志,你可以使用成员函数clear()。
文件定位
和 C的文件操作方式不同的是,C++ I/O系统管理两个与一个文件相联系的指针。一个是读指针,它说明输入操作在文件中的位置;另一个是写指针,它下次写操作的位置。每次执行输入或输出时, 相应的指针自动变化。所以,C++的文件定位分为读位置和写位置的定位,对应的成员函数是 seekg()和 seekp(),seekg()是设置读位置,seekp是设置写位置。它们最通用的形式如下:
设置指针位置
1 | istream &seekg ( pos_type position ); |
streamoff
定义于 iostream.h 中,定义有偏移量 offset 所能取得的最大值,seek_dir 表示移动的基准位置,是一个有以下值的枚举:
1 | ios::beg: 文件开头 |
这两个函数一般用于二进制文件,因为文本文件会因为系统对字符的解释而可能与预想的值不同。
示例:
1 | file1.seekg(1234,ios::cur);//把文件的读指针从当前位置向后移1234个字节 |
获取指针位置
tellg()
和 tellp()
这两个成员函数不用传入参数,返回pos_type 类型的值(根据ANSI-C++ 标准) ,就是一个整数,代表当前 get 流
指针的位置 (用tellg) 或 put 流
指针的位置(用tellp)。
流指针 get 和 put 的值对文本文件(text file)和二进制文件(binary file)的计算方法都是不同的,因为文本模式的文件中某些特殊字符可能被修改。由于这个原因,建议对以文本文件模式打开的文件总是使用seekg 和 seekp的第一种原型,而且不要对tellg 或 tellp 的返回值进行修改。对二进制文件,你可以任意使用这些函数,应该不会有任何意外的行为产生。
关闭文件
当文件读写操作完成之后,我们必须将文件关闭以使文件重新变为可访问的。关闭文件需要调用成员函数close()
,它负责将缓存中的数据排放出来并关闭文件。它的格式很简单:void close ();
这个函数一旦被调用,原先的流对象(stream object)就可以被用来打开其它的文件了,这个文件也就可以重新被其它的进程(process)所有访问了。
为防止流对象被销毁时还联系着打开的文件,析构函数(destructor)将会自动调用关闭函数close。
代码示例:
1 | /** |