开发工具-文件类

103 阅读6分钟

文件操作类

打开文件
关闭文件指针,并删除文件
调用fprintf向文件写入数据
从文件中读取以换行符"\n"结束的一行,类似fgets函数
从文件文件中读取一行
从文件中读取数据块
向文件中写入数据块
关闭文件指针

文件操作类声明

class CFile
{
private:
  FILE *m_fp;        // 文件指针
  bool  m_bEnBuffer; // 是否启用缓冲,true-启用;false-不启用,缺省是启用。
  char  m_filename[301]; // 文件名,建议采用绝对路径的文件名。
  char  m_filenametmp[301]; // 临时文件名,在m_filename后加".tmp"。

public:
  CFile();   // 构造函数。

  bool IsOpened();  // 判断文件是否已打开,返回值:true-已打开;false-未打开。

  // 打开文件。
  // filename:待打开的文件名,建议采用绝对路径的文件名。
  // openmode:打开文件的模式,与fopen库函数的打开模式相同。
  // bEnBuffer:是否启用缓冲,true-启用;false-不启用,缺省是启用。
  // 注意:如果待打开的文件的目录不存在,就会创建目录。
  bool Open(const char *filename,const char *openmode,bool bEnBuffer=true);

  // 关闭文件指针,并删除文件。
  bool CloseAndRemove();

  // 专为重命名而打开文件,参数与Open方法相同。
  // 注意:OpenForRename打开的是filename后加".tmp"的临时文件,所以openmode只能是"a"、"a+"、"w"、"w+"。
  bool OpenForRename(const char *filename,const char *openmode,bool bEnBuffer=true);
  // 关闭文件指针,并把OpenForRename方法打开的临时文件名重命名为filename。
  bool CloseAndRename();

  // 调用fprintf向文件写入数据,参数与fprintf库函数相同,但不需要传入文件指针。
  void Fprintf(const char *fmt,...);

  // 从文件中读取以换行符"\n"结束的一行,类似fgets函数。
  // buffer:用于存放读取的内容,buffer必须大于readsize+1,否则可能会造成内存的溢出。
  // readsize:本次打算读取的字节数,如果已经读取到了结束标志"\n",函数返回。
  // bdelcrt:是否删除行结束标志"\r"和"\n",true-删除;false-不删除,缺省值是false。
  // 返回值:true-成功;false-失败,一般情况下,失败可以认为是文件已结束。
  bool Fgets(char *buffer,const int readsize,bool bdelcrt=false);

  // 从文件文件中读取一行。
  // buffer:用于存放读取的内容,buffer必须大于readsize+1,否则可能会造成读到的数据不完整或内存的溢出。
  // readsize:本次打算读取的字节数,如果已经读取到了结束标志,函数返回。
  // endbz:行内容结束的标志,缺省为空,表示行内容以"\n"为结束标志。
  // 返回值:true-成功;false-失败,一般情况下,失败可以认为是文件已结束。
  bool FFGETS(char *buffer,const int readsize,const char *endbz=0);

  // 从文件中读取数据块。
  // ptr:用于存放读取的内容。
  // size:本次打算读取的字节数。
  // 返回值:本次从文件中成功读取的字节数,如果文件未结束,返回值等于size,如果文件已结束,返回值为实际读取的字节数。
  size_t Fread(void *ptr,size_t size);

  // 向文件中写入数据块。
  // ptr:待写入数据块的地址。
  // size:待写入数据块的字节数。
  // 返回值:本次成功写入的字节数,如果磁盘空间足够,返回值等于size。
  size_t Fwrite(const void *ptr,size_t size);

  // 关闭文件指针,如果存在临时文件,就删除它。
  void Close();

 ~CFile();   // 析构函数会调用Close方法。
};

文件操作类实现

CFile::CFile()   // 类的构造函数
{
  m_fp=0;
  m_bEnBuffer=true;
  memset(m_filename,0,sizeof(m_filename));
  memset(m_filenametmp,0,sizeof(m_filenametmp));
}

// 关闭文件指针
void CFile::Close() 
{
  if (m_fp==0) return;    // 判断空指针。
  fclose(m_fp);  // 关闭文件指针
  m_fp=0;
  memset(m_filename,0,sizeof(m_filename));

  // 如果存在临时文件,就删除它。
  if (strlen(m_filenametmp)!=0) remove(m_filenametmp);
  memset(m_filenametmp,0,sizeof(m_filenametmp));
}

// 判断文件是否已打开
bool CFile::IsOpened()
{
  if (m_fp==0) return false;    // 判断空指针。
  return true;
}

// 关闭文件指针,并删除文件
bool CFile::CloseAndRemove()
{
  if (m_fp==0) return true;    // 判断空指针。
  fclose(m_fp);  // 关闭文件指针
  m_fp=0;

  if (remove(m_filename) != 0) 
  { 
      memset(m_filename,0,sizeof(m_filename)); 
      return false; 
  }
  memset(m_filename,0,sizeof(m_filename));
  memset(m_filenametmp,0,sizeof(m_filenametmp));

  return true;
}

CFile::~CFile()   // 类的析构函数
{
  Close();
}

// 打开文件,参数与FOPEN相同,打开成功true,失败返回false
bool CFile::Open(const char *filename,const char *openmode,bool bEnBuffer)
{
  Close();
  if ( (m_fp=FOPEN(filename,openmode)) == 0 ) return false;
  
  memset(m_filename,0,sizeof(m_filename));
  STRNCPY(m_filename,sizeof(m_filename),filename,300);

  m_bEnBuffer=bEnBuffer;
  return true;
}

// 专为改名而打开文件,参数与fopen相同,打开成功true,失败返回false
bool CFile::OpenForRename(const char *filename,const char *openmode,bool bEnBuffer)
{
  Close();

  memset(m_filename,0,sizeof(m_filename));
  STRNCPY(m_filename,sizeof(m_filename),filename,300);
  
  memset(m_filenametmp,0,sizeof(m_filenametmp));
  SNPRINTF(m_filenametmp,sizeof(m_filenametmp),300,"%s.tmp",m_filename);

  if ( (m_fp=FOPEN(m_filenametmp,openmode)) == 0 ) return false;
  
  m_bEnBuffer=bEnBuffer;
  return true;
}

// 关闭文件并改名
bool CFile::CloseAndRename()
{
  if (m_fp==0) return false;    // 判断空指针。

  fclose(m_fp);  // 关闭文件指针
  m_fp=0;
  if (rename(m_filenametmp,m_filename) != 0)
  {
    remove(m_filenametmp);
    memset(m_filename,0,sizeof(m_filename));
    memset(m_filenametmp,0,sizeof(m_filenametmp));
    return false;
  }

  memset(m_filename,0,sizeof(m_filename));
  memset(m_filenametmp,0,sizeof(m_filenametmp));
  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从文件中读取一行,bDelCRT=true删除换行符,false不删除,缺省为false
bool CFile::Fgets(char *buffer,const int readsize,bool bdelcrt)
{
  if ( m_fp == 0 ) return false;
  memset(buffer,0,readsize+1);  // 调用者必须保证buffer的空间足够,否则这里会内存溢出。

  if (fgets(buffer,readsize,m_fp) == 0) return false;
  if (bdelcrt==true)
  {
    DeleteRChar(buffer,'\n'); DeleteRChar(buffer,'\r');  // 如果文件是windows格式,还要删除'\r'。
  }
  return true;
}

// 从文件文件中读取一行
// strEndStr是一行数据的结束标志,如果为空,则以换行符"\n"为结束标志。
bool CFile::FFGETS(char *buffer,const int readsize,const char *endbz)
{
  if ( m_fp == 0 ) return false;
  return FGETS(m_fp,buffer,readsize,endbz);
}

// 调用fread从文件中读取数据。
size_t CFile::Fread(void *ptr, size_t size)
{
  if ( m_fp == 0 ) return -1;
  return fread(ptr,1,size,m_fp);
}

// 调用fwrite向文件中写数据
size_t CFile::Fwrite(const void *ptr, size_t size )
{
  if ( m_fp == 0 ) return -1;
  size_t tt=fwrite(ptr,1,size,m_fp);
  if ( m_bEnBuffer == false ) fflush(m_fp);
  return tt;
}

用CFile类生成数据文件的用法

int main()
{
  CFile File;

  char strLocalTime[21];   // 用于存放系统当前的时间,格式yyyymmddhh24miss。
  memset(strLocalTime,0,sizeof(strLocalTime));
  LocalTime(strLocalTime,"yyyymmddhh24miss");  // 获取系统当前时间。
  
  // 生成绝对路径的文件名,目录/tmp/data,文件名:前缀(surfdata_)+时间+后缀(.xml)。
  char strFileName[301];
  SNPRINTF(strFileName,sizeof(strFileName),300,"/tmp/data/surfdata_%s.xml",strLocalTime);

  // 采用OpenForRename创建文件,实际创建的文件名例如/tmp/data/surfdata_20200101123000.xml.tmp。
  if (File.OpenForRename(strFileName,"w")==false)
  {
    printf("File.OpenForRename(%s) failed.\n",strFileName); return -1;
  }

  // 这里可以插入向文件写入数据的代码。
  // 写入文本数据用Fprintf方法,写入二进制数据用Fwrite方法。

  // 向文件中写入两行超女数据。
  File.Fprintf("<data>\n"\
     "<name>妲已</name><age>28</age><sc>火辣</sc><yz>漂亮</yz><memo>商要亡,关我什么事。</memo><endl/>\n"\
     "<name>西施</name><age>25</age><sc>火辣</sc><yz>漂亮</yz><memo>1、中国排名第一的美女;\n"\
     "2、男朋友是范蠡;\n"\
     "3、老公是夫差,被勾践弄死了。</memo><endl/>\n"\
     "</data>\n");

  //sleep(30);   // 停止30秒,用ls /tmp/data/*.tmp可以看到生成的临时文件。

  // 关闭文件指针,并把临时文件名改为正式的文件名。
  // 注意,不能用File.Close(),因为Close方法是关闭文件指针,并删除临时文件。
  // CFile类的析构函数调用的是Close方法。
  File.CloseAndRename();
}

用CDir类和CFile类处理数据文件的用法

int main()
{
  CDir Dir;

  // 扫描/tmp/data目录下文件名匹配"surfdata_*.xml"的文件。
  if (Dir.OpenDir("/tmp/data","surfdata_*.xml")==false)
  {
    printf("Dir.OpenDir(/tmp/data) failed.\n"); return -1;
  }

  CFile File;

  while (Dir.ReadDir()==true) //遍历匹配到所有文件
  {
    printf("处理文件%s...",Dir.m_FullFileName);

    if (File.Open(Dir.m_FullFileName,"r")==false)
    {
      printf("failed.File.Open(%s) failed.\n",Dir.m_FullFileName); return -1;
    }

    char strBuffer[301];

    while (true)
    {
      memset(strBuffer,0,sizeof(strBuffer));
      if (File.FFGETS(strBuffer,300,"<endl/>")==false) break; // 行内容以"<endl/>"结束。

      // printf("strBuffer=%s",strBuffer);

      // 这里可以插入解析xml字符串并把数据写入数据库的代码。
    }

    // 处理完文件中的数据后,关闭文件指针,并删除文件。
    File.CloseAndRemove();

    printf("ok\n");
  }
}