前言

我们在编程的过程中,文件的读写时经常遇到的一个问题,而在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,这样就可以区分字节值0XFFEOF

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 二进制文件读写

freadfwrite函数用于二进制输入和输出

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) ;
Last modification:July 12th, 2020 at 11:12 am
如果觉得我的文章对你有用,请随意赞赏