muduo源码解析12-fileutil命名空间

fileutil命名空间

fileutil命名空间主要是实现了对于文件的一系列操作
设计实现了两个类:ReadSmallFile和AppendFile
分别用于对文件的读和写操作

注意之前没有使用stringspiece头文件中的定义
因此所有用到stringpiece的东西全部换成std::string即可

ReadSmallFile类:

这个类主要就是实现了对于文件的读操作,可以把文件数据读到string中,也可以保存到类内部字符数组中去。

主要是关于文件操作的一些函数

ReadSmallFile成员变量:

private:
    int m_fd;                   //文件描述符
    int m_err;                  //错误码
    char m_buf[kBufferSize];    //保存数据的数组

//类中数组的大小
static const int kBufferSize=60*1024;  

需要注意最多只能读64KB的数据,超过64KB就只读64KB,因为内部字符数组大小有限

ReadSmallFile成员函数:

public:
    ReadSmallFile(string filename);
    ~ReadSmallFile();

    //把文件数据读到content中去,文件大小保存到filesize,修改时间保存到
    //modifyTime,创建时间保存到createTime
    template<typename string>
    int readToString(int maxsize,string* content,int64_t* filesize,
                     int64_t* modifyTime,int64_t* createTime);

    //把文件读到缓冲区中去,实际上读到类内部m_buf数组中去
    int readToBuffer(int* size);
    //返回类中m_buf数组
    const char* buffer() const;

构造函数里打开文件,析构函数里关闭文件。

AppendFile类:

这个类就与上面的那个类相对应,上面是读文件,这个就是写文件,只提供了最简单的append操作,就是把数据追加到文件尾部。

AppendFile成员变量:

private:
    FILE* m_fp;             //文件指针
    char m_buffer[64*1024]; //保存数据
    off_t m_writtenBytes;   //已经写入的字节数

搞不懂为啥又使用fopen,fclose等函数了,跟上面的open,close文件操作函数不一样了。。不过功能没啥区别

AppendFile成员函数:

public:
    //构造函数,打开一个文件
    explicit AppendFile(string filename);
    //关闭文件
    ~AppendFile();
    //向文件中写logline,长度为len
    void append(const char* logline,size_t len);
    //::fflush(m_fp)
    void flush();
    //返回已经写入文件的字节数
    off_t writtenBytes() const {return m_writtenBytes;}
private:
    //使用fwrite_unlocked来实现写文件
    size_t write(const char* logline,size_t len);

同样是构造函数里打开文件,析构函数里关闭文件,只提供了文件尾部追加数据,和清空缓冲区的操作。

fileutil头文件:

/*
fileutil命名空间主要是实现了对于文件的一系列操作
设计实现了两个类:ReadSmallFile和AppendFile
分别用于对文件的读和写操作

之前没有使用stringspiece头文件中的定义
因此所有用到stringpiece的东西全部换成std::string即可

*/

#ifndef FILEUTIL_H
#define FILEUTIL_H

#include"base/types.h"
#include"base/noncopyable.h"
//#include"base/stringpiece.h"
//stringpiece舍弃不用了,自己用std::string来实现欠缺的功能

namespace mymuduo {

namespace fileutil {

//顾名思义,读取小文件的类
class ReadSmallFile:noncopyable
{
public:
    ReadSmallFile(string filename);
    ~ReadSmallFile();

    //把文件数据读到content中去,文件大小保存到filesize,修改时间保存到
    //modifyTime,创建时间保存到createTime
    template<typename string>
    int readToString(int maxsize,string* content,int64_t* filesize,
                     int64_t* modifyTime,int64_t* createTime);

    //把文件读到缓冲区中去,实际上读到类内部m_buf数组中去
    int readToBuffer(int* size);
    //返回类中m_buf数组
    const char* buffer() const;
    //类中数组的大小
    static const int kBufferSize=60*1024;

private:
    int m_fd;                   //文件描述符
    int m_err;                  //错误码
    char m_buf[kBufferSize];    //保存数据的数组

};

//利用ReadSmallFile中的readToString函数完成readFile函数
template<typename string>
int readFile(string filename,int maxSize,string* content,
             int64_t* fileSize=NULL,int64_t* modifyTime=NULL,int64_t* createTime=NULL)
{
    ReadSmallFile file(filename);
    return file.readToString(maxSize,content,fileSize,modifyTime,createTime);
}

class AppendFile:noncopyable
{
public:
    //构造函数,打开一个文件
    explicit AppendFile(string filename);
    //关闭文件
    ~AppendFile();
    //向文件中写logline,长度为len
    void append(const char* logline,size_t len);
    //::fflush(m_fp)
    void flush();
    //返回已经写入文件的字节数
    off_t writtenBytes() const {return m_writtenBytes;}
private:
    //使用fwrite_unlocked来实现写文件
    size_t write(const char* logline,size_t len);
    FILE* m_fp;             //文件指针
    char m_buffer[64*1024]; //保存数据
    off_t m_writtenBytes;   //已经写入的字节数
};

}//namespace fileutil

}//namespace mymuduo

#endif // FILEUTIL_H

fileutil源文件:

#include "fileutil.h"

#include"base/logging.h"

#include<assert.h>
#include<errno.h>
#include<fcntl.h>
#include<stdio.h>
#include<sys/stat.h>
#include<unistd.h>

namespace mymuduo{

namespace fileutil {

//构造函数中执行open打开文件操作
ReadSmallFile::ReadSmallFile(string filename)
    :m_fd(::open(filename.data(),O_RDONLY|O_CLOEXEC)),m_err(0)
{
    m_buf[0]='\0';
    //文件若是打开失败,设置错误码信息
    if(m_fd<0)
        m_err=errno;
}

//析构函数中关闭文件
ReadSmallFile::~ReadSmallFile()
{
    if(m_fd)
        ::close(m_fd);
}

//把文件数据读入到content中,大小保存到filesize,修改时间保存到modifyTime
//创建时间保存到createTime
template <typename string>
int ReadSmallFile::readToString(int maxsize,string* content,
                                int64_t* filesize,int64_t* modifyTime,int64_t* createTime)
{
    static_assert (sizeof(off_t)==8,"_FILE_OFFSET_BITS = 64" );
    assert(content!=NULL);//保证可以传入到content中

    if(m_fd<0)
        return m_err;   //保证文件描述符正确

    int err=m_err;      //返回值err,可根据判断是否出错
    content->clear();   //先清空缓冲区

    if(filesize)        //保证filesize不是NULL,可以传入
    {
        //该结构体用于获取文件信息,文件大小,修改时间,创建时间等信息
        struct stat statbuf;
        if(::fstat(m_fd,&statbuf)==0)   //获取文件信息
        {
            if(S_ISREG(statbuf.st_mode)) //是否是一个常规文件
            {
                *filesize=statbuf.st_size;  //保存文件大小
                //重新分配一下string的内存空间,保证为min(maxsize,*filesize)
                content->reserve(static_cast<int>(std::min(
                                                      implicit_cast<int64_t>(maxsize),*filesize)));
            }else if(S_ISDIR(statbuf.st_mode))  //是否是一个目录
                err=EISDIR;
            //保存文件修改和创建时间
            if(modifyTime)
                *modifyTime=statbuf.st_mtime;
            if(createTime)
                *createTime=statbuf.st_ctime;

        }else
            err=errno;
    }

    //开始拷贝文件数据到string中
    while(content->size()<implicit_cast<size_t>(maxsize))
    {
        //一次要读取的数据量=min(maxsize-content->size(),60*1024)
        size_t toRead=std::min(implicit_cast<size_t>(maxsize)-content->size(),
                               sizeof(m_buf));
        //实际读取了n个字节的数据量
        ssize_t n=::read(m_fd,m_buf,toRead);
        if(n>0)
            content->append(m_buf,n);
        else
        {
            if(n<0)err=errno;
            break;
        }
    }

    return err; //返回状态信息,可判断是否有出错
}

int ReadSmallFile::readToBuffer(int *size)
{
    int err=m_err;
    if(m_fd<=0)return err;
    ssize_t n=::pread(m_fd,m_buf,sizeof (m_buf)-1,0);
    if(n>=0)
    {
        if(size)
            *size=static_cast<int>(n);//保存大小
        m_buf[n]='\0';//数组最后结束符'\0'
    }else
        err=errno;
    return err;
}

AppendFile::AppendFile(string filename)
    :m_fp(::fopen(filename.data(),"ae")),   //e for O_CLOEXEC
      m_writtenBytes(0)
{
    assert(m_fp);   //保证写入的文件确实存在
    ::setbuffer(m_fp,m_buffer,sizeof(m_buffer));
}

AppendFile::~AppendFile()
{
    ::fclose(m_fp);
}

void AppendFile::append(const char *logline, size_t len)
{
    //实际写入了n个字节
    size_t n=write(logline,len);
    size_t remain=len-n;    //剩余remain个字节
    while(remain>0)         //保证全部写入
    {
        size_t x=write(logline+n,remain);
        if(x==0)
        {
            int err=ferror(m_fp);
            if(err)
                fprintf(stderr, "AppendFile::append() failed %s\n", strerror_tl(err));
            break;
        }
        n+=x;
        remain=len-n;
    }
    m_writtenBytes+=len;
}

void AppendFile::flush()
{
    ::fflush(m_fp);
}

size_t AppendFile::write(const char *logline, size_t len)
{
    return ::fwrite_unlocked(logline,1,len,m_fp);
}

//提供了两个模板
template int readFile(string filename,int maxSize,string* content,
        int64_t* ,int64_t*,int64_t*);
template int ReadSmallFile::readToString(int maxSize,string* content,int64_t*,
        int64_t*,int64_t*);

}//namespace fileutil

}//namespace mymuduo

测试:

#include"base/fileutil.h"
#include<iostream>
using namespace std;

namespace mymuduo{
namespace currentthread {
void cacheTid()
{
}
}
}


int main()
{

    //添加数据到文件./test.txt中
    /*mymuduo::fileutil::AppendFile apf("./test.txt");
    string data="stat 结构定义于:/usr/include/sys/stat.h 文件中";
    apf.append(data.data(),data.size());
    */

    //从./test.txt中读取数据
    mymuduo::fileutil::ReadSmallFile rdf("./test.txt");
    string strbuf;
    int64_t filesize=0,modifyTime=0,createTime=0;
    rdf.readToString(64*1024,&strbuf,&filesize,&modifyTime,&createTime);

    std::cout<<strbuf<<std::endl<<filesize<<" "<<
               modifyTime<<" "<<createTime<<std::endl;

    std::cout<<"over...\n";
}

 

需要注意,只有在apf对象和rdf对象析构时文件才能正确关闭,这两个类都没有提供close函数,只允许在对象析构时关闭文件。

posted @ 2020-08-25 01:21  WoodInEast  阅读(257)  评论(0)    收藏  举报