经常要用到文件操作,所以想要将文件操作封装起来,方便后面的使用。
文件操作包含的基本操作有:打开文件,对文件进行写操作,对文件进行读操作,还有关闭文件。现在就对这几个操作封装。
1.成员变量
如果要进行封装,那么成员变量有哪些?这是一个文件基本操作类,这些基本操作都要用到的变量是什么?
1.1 文件指针
先分析一下这些基本操作有什么特点。
FILE *fopen( const char * filename, const char * mode ); // 打开文件,返回文件指针
int fprintf(FILE *fp, const char *format, ...); // 写操作
char *fgets(char *buf, int size, FILE *fp); //读操作
int fclose(FILE *fp); // 关闭文件
会发现这些操作都离不开一个变量,那就是文件指针,所以它就作为成员变量。
1.2 文件缓冲区
对操作文件的时候,C语言为文件分配一个信息区,该信息区包含文件描述信息、缓冲区位置、缓冲区大小、文件读写到的位置等基本信息。文件进行读写操作,是先将数据读写到缓冲区中,所以第2个成员变量是缓冲区。
private:
FILE *m_fp; // 文件指针
bool m_bEnBuffer; // 是否启用缓冲区,true-启用;false-不启用
2.成员函数
成员函数,首先想到的便是构造函数和析构函数。文件的基本操作有打开和关闭文件,对文件进行读和写。分析了应该具有什么函数,接下来就来分析怎么来声明这些函数。一个函数包括:函数名,返回值,参数列表,还有函数体
2.1 构造函数
构造函数负责对类的成员变量进行初始化
CFile::CFile() // 类的构造函数
{
m_fp=0;
m_bEnBuffer=true;
}
2.2 析构函数
析构函数通常用来释放资源,但是释放资源我将它放在Close函数里面
CFile::~CFile() // 类的析构函数
{
Close(); // 调用Close释放资源
}
2.3 Close函数
这个函数通常是用来关闭文件的,一般不会有错误,所以可以不用返回值。因为它只是关闭文件,只用到文件指针这个变量,而Close()和文件指针是在同一个类里面的,所以不需要到其他的变量,所以参数列表为空。
// 关闭文件指针
void CFile::Close()
{
if (m_fp!=0) fclose(m_fp); // 关闭文件指针
m_fp=0;
}
2.4 Open函数
这个函数是用来打开文件的。
(1)分析一下返回值,打开文件,有时候可能会打开失败,所以要有一个返回值来提示调用者是否打开文件成功。这个返回值用bool类型的就可以了,返回true和false就很直接明了。
(2)函数的参数:这个函数是用来打开文件的,要有文件名(文件在这封装没有作为成员变量)才能打开,调用的时候调用者要给它一个文件名。所以参数列表要有文件名(文件名一般是字符串类型)。
调用者在调用的时候除了要指定文件名,还应该有什么?还应该有一个打开的模式。所以另一个参数是打开的模式。
(3)函数体就是利用库函数里面的fopen函数打开文件,不过还要注意一些细节的问题。比如说要打开新的文件,但是已经打开了别的文件,就要先关闭。
// 打开文件,参数与fopen相同,打开成功true,失败返回false
bool CFile::Open(const char *filename,const char *openmode)
{
Close(); // 打开新的文件之前,如果已经打开了文件,关闭它。
if ( (m_fp=fopen(filename,openmode)) == 0 ) return false;
return true;
}
2.5 Fprintf函数
这个函数用来向文件写入文本数据。
(1)函数的返回值,这里偷懒一下,设置为void。哈哈哈
(2)参数是可变参数
(3)函数体:注意细节,这个函数要对指针进行操作,所以要判断一下指针是否为空,防止对空指针操作。
// 调用fprintf向文件写入数据
void CFile::Fprintf(const char *fmt,...)
{
if ( m_fp == 0 ) return;
va_list arg;
va_start(arg,fmt);
vfprintf(m_fp,fmt, arg);
va_end(arg);
if ( m_bEnBuffer == false ) fflush(m_fp);
}
2.6 Fgets 函数
这个函数用于从文件中读取一行。
(1)返回值:设置为bool类型,便于查看是否读取成功。
(2)参数列表:从文件中读取一行数据,那么这一行数据要有地方来存放,所以设置一个字符串来暂时存放读取出来的这一行数据。
还有一个问题这一行数据有多大,还要设置一个参数来限定每次读取的大小。
(3)函数体:要对指针操作,所以要防止对空指针进行操作。
还有一个用于暂时存放数据的字符串(每次取出来都放在它这里),所以每次是以前记得初始化。
// 调用fgets从文件中读取一行
bool CFile::Fgets(char *strBuffer,const int ReadSize)
{
if ( m_fp == 0 ) return false;
memset(strBuffer,0,ReadSize);
if (fgets(strBuffer,ReadSize,m_fp) == 0) return false;
return true;
}
3.CFile类及测试代码
3.1 封装好的类
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
// 文件操作类声明
class CFile
{
private:
FILE *m_fp; // 文件指针
bool m_bEnBuffer; // 是否启用缓冲区,true-启用;false-不启用
public:
CFile(); // 类的构造函数
~CFile(); // 类的析构函数
// 打开文件,参数与fopen相同,打开成功true,失败返回false
bool Open(const char *filename,const char *openmode);
// 调用fprintf向文件写入数据
void Fprintf(const char *fmt,... );
// 调用fgets从文件中读取一行
bool Fgets(char *strBuffer,const int ReadSize);
// 关闭文件指针
void Close();
};
CFile::CFile() // 类的构造函数
{
m_fp=0;
m_bEnBuffer=true;
}
// 关闭文件指针
void CFile::Close()
{
if (m_fp!=0) fclose(m_fp); // 关闭文件指针
m_fp=0;
}
CFile::~CFile() // 类的析构函数
{
Close(); // 调用Close释放资源
}
// 打开文件,参数与fopen相同,打开成功true,失败返回false
bool CFile::Open(const char *filename,const char *openmode)
{
Close(); // 打开新的文件之前,如果已经打开了文件,关闭它。
if ( (m_fp=fopen(filename,openmode)) == 0 ) return false;
return true;
}
// 调用fprintf向文件写入数据
void CFile::Fprintf(const char *fmt,...)
{
if ( m_fp == 0 ) return;
va_list arg;
va_start(arg,fmt);
vfprintf(m_fp,fmt, arg);
va_end(arg);
if ( m_bEnBuffer == false ) fflush(m_fp);
}
// 调用fgets从文件中读取一行
bool CFile::Fgets(char *strBuffer,const int ReadSize)
{
if ( m_fp == 0 ) return false;
memset(strBuffer,0,ReadSize);
if (fgets(strBuffer,ReadSize,m_fp) == 0) return false;
return true;
}
3.2 测试代码
int main(int argc,char *argv[])
{
if (argc !=2) { printf("请输入待打开的文件名。\n"); return -1; }
CFile File;
if (File.Open(argv[1],"r")==false) { printf("File.Open(%s)失败。\n",argv[1]); return -1; }
char strLine[301];
while (true)
{ // 从文件中读取每一行
if (File.Fgets(strLine,300)==false) break;
printf("%s",strLine); // 把从文件中读到的内容显示到屏幕
}
}
测试结果