前言
我们在编程的过程中,文件的读写时经常遇到的一个问题,而在c++中有多种方式对文件进行读写操作,这篇文章我将对以下几个部分做详细的介绍和总结:
- 基于C的文件读写
- 基于C++的文件读写
一、C文件读写
1.1 打开文件
FILE *fopen(const char * filename, const char * mode)
其中,filename为C类型字符串(如果filename为string类型,那么在这里需要将string转为C类型的字符串,使用c_str()
函数),用来代表文件。mode为访问文件的方式:
模式 | 用途 |
---|---|
r | 打开一个已有的文本文件,只允许读 |
w | 打开一个文本文件,只允许写。如果不存在,则新创建一个。如果存在,默认会从头开始写,以前的内容也被覆盖。 |
a | 打开一个文本文件,以追加模式写入文件。如果文件不存在,则新建一个。 |
r+ | 打开一个文本文件,允许读写。 |
w+ | 打开一个文本文件,允许读写。如果不存在,则新创建一个。如果存在,默认会从头开始写,以前的内容也被覆盖。 |
a+ | 打开一个文本文件,允许读写。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。 |
注意,如果处理的是二进制的文件,比如图像。需要用以下的模式对应替代上表的模式来进行访问:
"rb","wb","ab","rb+","wb+","ab+"
1.2 关闭文件
int fclose(FILE * file);
1.3 文本文件读写
char* fgets(char* buf, int n, FILE * file);
函数fgets()
从file输入流中读取n-1
个字符,把读取的字符串复制到缓冲区buf,并在最后追加一个NULL
字符来终止字符串。注意,如果遇到换行符\n
或者是文件末尾EOF
,函数会停止读取。
int fgetc(FILE * file);
函数fgetc()
从file输入文件中读取一个字符,返回值是读取的字符,如果读到末尾返回EOF。那么为什么函数原型返回值为int呢?
首先,EOF
其实是一个宏,即#define EOF -1
,那么如果用unsigned char 来表示的话,-1是负值,无符号无法表示。而用char表示的话,-1在char类型中的值是0XFF
,如果文件中恰好有值是0XFF
,那这个时候就会误判为文件结束,不正确。因此,这里返回值为4字节的int,-1用int表示为OXFFFFFFFF
,如果读取到0XFF
,返回的int为0X000000FF
,这样就可以区分字节值0XFF
和EOF
。
example
string filename = "../docs/test.txt"; //文本文件
FILE * file = fopen(filename.c_str(), "r");
char * buffer = new char[54]; //fgets读取的字节都缓冲到buffer中
fgets(buffer, 54, file); //需要注意的是,fgets()会在遇到换行符之后就停止读取,因此
int i = 0; //如果要读取文件所有内容,可以使用fgetc()函数
while(buffer[i] != NULL){
cout<<buffer[i++]<<"\t"<<endl;
}
/*********************************/
//使用fgetc()函数读取文件所有字节
char c; //注意,这里需要将fgetc()的返回值赋给char型,虽然函数原型是int,原因就不再做解释了
while((c = fgetc(file)) != EOF){
cout<<c;
}
fclose(file);
int isClose = fclose(file);
if(isClose == 0)
cout<<"成功关闭";
else if(isClose == EOF)
cout<<"关闭失败";
1.4 二进制文件读写
fread
和fwrite
函数用于二进制输入和输出
size_t fread(const void* buffer, size_t size, size_t count, FILE* file);
--buffer : 指向数据块的指针
--size : 每个数据块的大小
--count : 数据个数
--file : 文件指针
size_t fwrite(const void* buffer, size_t size, size_t count, FILE* file);
--buffer : 指向数据块的指针
--size : 每个数据块的大小
--count : 数据个数
--file : 文件指针
example
string filename = "../images/bmp_test.bmp";
FILE * imageFile = fopen(filename.c_str(), "rb");
char * buffer = new char[54];
fread(buffer, sizeof(char), 54, imageFile); //注意此处的写法与函数原型的定义对应理解
fclose(imageFile);
1.5 FILE常用其他函数
/* fseek()把文件指针移到指定位置*/
int fseek(FILE *fp,long offset,int origin)
--origin : 指针起始点
--offset : 偏移字节数
/* rewind()将文件指针定义到文件头*/
rewind(FILE * file)
/* remove()删除指定文件,成功返回0,失败返回-1*/
int remove(char * filename)
二、C++文件读写
首先,我们大致总结以下上面这张图的内容(图中的箭头代表类的继承关系)。
- istream : 输入流类型,提供输入操作。
- ostream : 输出流类型,提供输出操作。
- iostream : 用于读写流的基本类型。
- fstream : 读写文件的基本类型。
- sstream : 读写内存string对象的类型。
由于本文主要讨论文件读写,因此在这里着重讨论fstream
的内容。
2.1 打开文件
fstream file;
file.open(const char * filename, ios_base:openmode mode = ios_base::in | ios_base :: out);
fstream file(const char * filename, ios_base:openmode mode = ios_base::in | ios_base :: out);
以上两种文件打开方式都是正确的。
filename
为文件名,mode
为打开文件的方式。mode有以下几种方式:
ios::in | 读文件 |
---|---|
ios::out | 写文件 |
ios::ate | 打开文件后立即定位到文件尾 |
ios::app | 每次写操作前均定位到文件末尾 |
ios::trunc | 如果文件已经存在则删除文件 |
ios::binary | 打开二进制文件 |
指定文件模式有如下限制:
- 默认情况下,以out模式打开文件会清楚文件内的已有数据,因此我们必须同时指定app模式,这样只会将数据追加到文件末尾。
- ate和binary模式可用于任何类型的文件流对象,且可以与其他任何文件模式组合使用。
可以通过调用成员函数 is_open()
来检查一个文件是否被顺利打开
bool is_open();
2.2 关闭文件
当文件读写操作完成之后,我们必须将文件关闭以使文件重新变为可访问的。关闭文件需要调用成员函数close(),它负责将缓存中的数据排放出来并关闭文件。它的格式很简单:
void close();
2.3 文本文件读写
-
读文件:
>>
、getline()
、get()
>>
:以空格、Tab、换行等符号结束。getline
:一次读入一行
/* 从文件读取num-1个字符到buffer(内存)中*/ istream &getline( char *buffer, streamsize num );
get
:一次读取一个字符
istream& get(char &c);
-
写文件:
<<
、put()
<<
:一次写入一行put
:一次写入一个字符
2.4 状态标识符的验证
-
eof()
如果读文件到达文件尾,返回true
-
bad()
如果在读写过程中出错,返回 true
-
fail()
除了与bad() 同样的情况下会返回 true 以外,加上格式错误时也返回true
2.5 获得和设置流指针
-
seekg()
和seekp()
/*从文件头开始,到position个字节的位置*/ seekg ( pos_type position ); seekp ( pos_type position ); /*从direction开始,偏移offset个字节*/ seekg ( off_type offset, seekdir direction ); seekp ( off_type offset, seekdir direction );
三、二进制文件的读写
文件打开的默认模式是文本文件,要想读写二进制文件,需要在打开时显示声明ios::binary
3.1 使用get()
和put()
读写一个字符
3.2 使用read()
和write()
进行读写
-
read()
/*从文件中提取 n 个字节数据,写入buf*/ istream & read (char * buf, int n);
-
write()
/*把buf指向的内容取n个字节写入文件*/ ostream & write (char * buf , int n) ;