当前位置:首页 » 《关注互联网》 » 正文

【C/C++】一文带你从零手搓《文件管理》工具

0 人参与  2024年12月31日 14:01  分类 : 《关注互联网》  评论

点击全文阅读



每日激励:“困难像弹簧,你弱它就强;你强它就弱。”

绪论​:
本章是仿RBMQ项目的分支篇章(后面会更),该文章主要写到对文件增、删、查、改以及一些文件其他操作和目录的创建删除操作,通过写这些文件/目录接口,来直接能通过这个文件类,然后调用文件类中的函数,从而实现快速方便的管理自己的文件。
————————
早关注不迷路,话不多说安全带系好,发车啦(建议电脑观看)。

File文件操作工具

判断文件是否存在文件大小文件读/写文件创建/删除目录创建/删除

项目操作(仿MQ)
helper.hpp中创建类:FileHelper
构造:(文件名):初始化文件名

学习对文件操作的函数

1. 判断文件是否存在函数

1. 使用判断文件状态的函数stat,来判断文件是否存在int stat(const char* path,struct stat* buf)返回值:文件存在返回0,反之返回-1,并且得到buf文件属性结构1. path:文件名2. buf:一个输出型参数,通过该参数得到访问 文件的状态 传入struct stat结构体int stat(const char* path,struct stat* buf);struct stat { dev_t     st_dev;     /* ID of device containing file */ ino_t     st_ino;     /* inode number */ mode_t    st_mode;    /* protection */ nlink_t   st_nlink;   /* number of hard links */ uid_t     st_uid;     /* user ID of owner */ gid_t     st_gid;     /* group ID of owner */ dev_t     st_rdev;    /* device ID (if special file) */ off_t     st_size;    /* total size, in bytes */ blksize_t st_blksize; /* blocksize for file system I/O */ blkcnt_t  st_blocks;  /* number of 512B blocks allocated */ time_t    st_atime;   /* time of last access */ time_t    st_mtime;   /* time of last modification */ time_t    st_ctime;   /* time of last status change */}该结构体中就包含着许多文件的属性如:文件大小:st_size;

2. 修改文件名称函数

int rename(const char *oldpath, const char *newpath);返回值:成功返回0(直接判断返回值是否0)1.oldpath:旧文件名(需要修改的文件)2.newpath:新文件名

3. 删除文件函数

int remove(const char *pathname);1. pathname:文件名返回值:成功返回0,失败返回-1

4. 打开文件函数

#include <fstream>explicit fstream (const char* filename, ios_base::openmode mode = ios_base::in | ios_base::out);filename:打开的文件名mode:打开文件的操作

5. 文件的读取

直接使用fstream类的读取类函数read()istream& read (char* s, streamsize n);1. s:要存储读取数据的地址(输出型参数,也就是吧地址传进去,最终函数会将数据写道改地址上,注意提 前申请空间!)2. n:要提取的字符数是一个有符号整型。设置读位置类函数:fstream::seekg()istream& seekg(streamoff offset,seek_dir origin);  1.offset:偏移量所能取得的最大值,2.seek_dir:表示移动的基准位置offset:1. ios::beg: 文件开头2. ios::cur: 文件当前位置3. ios::end: 文件结尾

6. 创建目录

int mkdir(const char *pathname, mode_t mode);成功返回0,反之非01. pathname:目录名2. mode:权限

7. 使用system可以执行指令

system就是调用系统命令行cmd(在Linux下的shell指令),输入为字符串,然后把这个字符串输出给命令行,让命令行执行。

头文件:#include <sys/stat.h>、 #include <sys/types.h>int system(const char* command);返回值:失败返回-11. command:要命令行中执行的指令

实现逻辑:

类: FileHelper
构造: FileHelper(文件名):初始化文件名

成员变量:

文件名(_filename)

成员函数(实现步骤):

文件是否存在函数:

使用库函数:使用 stat 判断文件是否存在创建结构体文件状态结构体(struct stat)调用stat(路径path,状态结构的指针 stat)此时直接通过获取状态的做法,判断文件是否存在存在返回0,反之返回-1

文件大小:size_t size()

先判断文件是否存在失败返回0返回st中的st_size变量

文件的读(返回bool判断是否读取成功):

从offset开始读取len长度的数据
read(body,偏移量offset、读取长度len) 打开文件 使用ifstream ifs(文件名,权限 二进制 | 读权限)打开ifs调用is_open判断是否打开成功失败ELOG: %s 文件打开失败!,filename;返回false 为body申请len大小的空间(后续要将读取的数据 存到 body中)跳转文件读写位置 ifs使用seekg(偏移量,设置为文件起始位置开始) 读取指定长度的文件数据 ifs使用read读取(缓冲区首地址,长度)判断read是否成功调用good函数判断(成功返回true)ELOG:%s 文件读取数据失败 filenameifs关闭文件close,返回false 关闭文件 关闭文件,返回true read(string* body) 获取fsize文件大小(根据文件大小调整body空间)获取文件大小(使用内置rsize函数)返回:调用readFile函数(&body[0],起始位置开始,body大小)

文件写:

从指定offset偏移量位置读取len长度的数据
writeFile(要写的内容body,偏移量offset,长度len); 打开文件 和上面方法一样,但使用fstream(因为ofstream不具备文件读操作权限,就无法跳转,所以使用读写类:fstream)fstream(文件名,二进制 | 读 | 写)判断是否成功…(同上) 跳转到文件指定位置写 使用seekg 写入数据 使用write(写入缓冲区body,长度)判断是否写入成功失败打印:ELOG %s 文件写入数据失败 filename关闭文件,返回false 关闭,返回true write(要写的内容body) 调用writeFile(body,起始位置开始,body大小)

修改文件名称:rename(nname)

调用库函数:int rename(const char *oldpath, const char *newpath);直接调用::rename(o,n)函数(前面加上 :: 代表使用全局的,而非局部的)成功返回0(直接判断返回值是否0)

文件的创建:createFile

直接复用write中fstream打开文件方法(打开不成功,就会自动创建,并且还不需要读权限)关闭文件返回true

删除文件:removeFile

使用库函数remove成功返回0失败返回-1

创建目录:createDirectory(路径)

int mkdir(const char *pathname, mode_t mode);头文件:

需要逐层创建,对传进来的目录不断的截断创建,因为要防止,前面的目录不存在:
例如:/aaa/bb/cc/ddd.hpp,需要从第一个父级目录开始创建
因此:从前完后不断的找到 “/” 进行截断,然后再创建目录
执行具体过程:aaa/、aaa/bbb/、aaa/bbb/cc/、
最终在后面找不到"/"了那么就创建文件:
createFIle(aaa/bbb/cc/ddd.hpp)

循环,从前往后find: “/”,(使用两变量:idx记录查找到了那个字符串、pos记录查找到的 “/” 的位置)若为pos == npos:表示没找到,即前面的目录肯定存在了。 创建文件: … 否则表示找到“/”,“创建”逐层目录 subpath = 使用substr获取目录子串(从0 ~ pos)创建目录:mkdir(目录名,权限0775),(成功返回0,失败返回-1)判断若创建失败, ELOG 创建目录 %s 失败 subpath return false其中在失败中,返回-1的情况还有可能是因为文件已存在,对于这种情况是正常的所以不列入错误中,要避免:存在时errno会被设置为 EEXIST。 idx改成pos向后偏移,循环

移除目录

使用system函数执行rm -rf指令(库函数平台一致性)
2. 创建cmd变量存 删除文件指令调用system(cmd) ,返回是否成功(若返回值为-1则失败)

获取文件的父级目录:parentDirectory()(静态的函数)

/aa/bb/cc/d/test.txt(从后往前找到一个/,其前面就是父级目录)pos记录filename中从后往前寻找(find_last_of)/如果未找到,表示当前路径就是最终目录,返回./找到后:截取0 ~ pos位置的父级路径给到path,并返回

文件工具源码:

FileHelper.hpp

class FileHelper{    public:        FileHelper(const std::string& filename):_filename(filename){        }        /**         * 判断路径是否存在         */        bool exists(){            //使用stat函数            struct stat st;            return (stat(_filename.c_str(),&st) == 0);        }        /**         * 判断文件大小,直接通过获取的文件状态返回的对象st         * 获取其内部成员 st_size 即是文件的大小         */        size_t sizeFile(){            struct stat st;            stat(_filename.c_str(),&st);            return  st.st_size;        }        bool renameFile(const std::string& newfilename){            //使用函数rename            return (std::rename(_filename.c_str(),newfilename.c_str()) == 0);//返回0表示修改成功        }        static bool removeFile(const char* filename){            return ::remove(filename) == 0;//返回0表示删除成功        }        static bool createFile(const char* filename){            std::fstream fs(filename, std::ios::binary | std::ios::out);            //binary二进制打开防止数据出现问题,            //out:写权限            return fs.is_open();//通过打开的文件判断是否打开成功        }        /**         * 文件的读取         * 将读取的文件中的所有内容放进buf中         * buf:输出型参数,存要读取的数据         */        bool readFile(std::string& buf,size_t offset,size_t len){            //1. 打开文件,调整buf空间            std::ifstream fs(_filename,std::ios::binary | std::ios::in);//二进制和读方式打开文件filename            //调整buf空间            buf.resize(len);                // 判断是否打开成功            if(!fs.is_open()){                ELOG("%s,文件打开失败!",_filename.c_str());                return false;            }            //2. 从当前位置读取所有内容放到buf中                //2.1 偏移到文件的指定位置            fs.seekg(offset,std::ios::beg);//从文件开始beg,偏移到offset                //2.2 读取所有内容给到buf            fs.read(&buf[0],len);//                        if(fs.good() == false){//判断上一步读取是否成功                ELOG("%s,文件读取数据失败",_filename.c_str());                return false;            }            fs.close();            return true;        }        bool readFile(std::string& buf){            //获取文件大小            size_t filesize = sizeFile();            //调整body空间:resize            buf.resize(filesize);            return readFile(buf,0,filesize);        }        /**         * 文件的写入         * 将读取的文件中的所有内容放进buf中         * body:所要写的内容         * offset:偏移量         * len:写入长度         */        bool writeFile(const std::string& body,size_t offset,size_t len){            //1. 打开文件            std::ofstream fs(_filename.c_str(),std::ios::binary | std::ios::out | std::ios::in);                // 判断是否打开成功            if(!fs.is_open()){                ELOG("%s,文件打开失败!",_filename.c_str());                return false;            }            //2. 跳到指定位置写:            fs.seekp(offset,std::ios::beg);            //3. 写            fs.write(body.c_str(),len);            if(fs.good() == false){                ELOG("%s,文件写入数据失败",_filename.c_str());                return false;            }            //3. 关闭            fs.close();            return true;        }        bool writeFile(const std::string& body){            return writeFile(body,0,body.size());                }        static bool createDirectory(const std::string& path){            //循环创建            size_t pos = -1,idx = 0;            //pos查找/位置            pos = path.find_first_of("/");            while(pos != std::string::npos)            {                std::string subpath = path.substr(0,pos);//每次从 0 ~ pos(因为要保证层级结构)                int ret = mkdir(subpath.c_str(),0775);//创建目录 ,并设置 权限为 775                 if(ret != 0 && errno != EEXIST){                    ELOG("创建目录 %s 失败",subpath.c_str());                    return false;                }                idx = pos + 1;//idx记录未获取的目录的 / 分割的字符串起始位置                pos = path.find_first_of('/',idx);            }            //当出来时,就表示找不到 "/"            //也就表示 前面的目录 即使不存在也会被创建好了            return mkdir(path.c_str(),0775) == 0;///        }        static bool removeDirectory(const std::string& path){            //此处使用stytem来执行 rm -rf 指令进行删除操作,这样就能减代码量(就不在需要再去写删除多层目录的代码)            std::string cmd = "rm -rf " + path;            return system(cmd.c_str()) != -1;        }        static std::string parentDirectory(const std::string& path){            //获取父目录            //直接从后往前找到 / 前面就是父级目录            int pos = path.find_last_of("/");            if(pos == std::string::npos){                //表示当前路径就是最终目录,返回./                return "./";            }            return path.substr(0,pos);        }    private:        std::string _filename;};

项目操作(仿MQ):
再mqtest目录下进行测试:
创建mq_filetest.cpp文件进行测试文件
引用helper.hpp

测试:

主函数内:

实例化文件工具类 helper(文件名 …/mqcommon.logger.hpp)判断该文件是否存在,进行输出DLOG 是否存在 %d existsFile(直接调用类函数)DLOG 文件大小 %d sizeFile(直接调用类函数)实例化文件工具类 tmp_helper ./aaa/bbb/ccc/tmp.hpp(创建)判断文件是否存在(返回bool)若不存在, 首先获取父级目录parentDirectory,(将文件和目录的创建和删除函数修改为静态的,方便使用,其中参数(文件名)需要自己传入)判断目录是否存在(使用文件工具类匿名函数调用静态后函数exists判断):不存的话在进行创建目录createDirectory最后在创建文件createFile( …/tmp.hpp)(在创建目录中修改下,需要注意的是当文件已存在时的情况, 它也会返回-1且errno 会被设置为 EEXIST,若文件已存在不是失败情况,所以对于失败情况的判断需要重新写)在发次发现问题,当没有找到“/”的情况下,文件创建我们应该是从根目录开始,而不是直接截取最后的路径(这样会导致创建到当前文件中) 进行读写测试: DLOG(打印查看) 局部数据的读取和写入 读取文件(…/tmp.hpp)中的 offset位置的len长度写入 … rename删除文件、删除目录

本章完。预知后事如何,暂听下回分解。

如果有任何问题欢迎讨论哈!

如果觉得这篇文章对你有所帮助的话点点赞吧!

持续更新大量C++细致内容,早关注不迷路。


点击全文阅读


本文链接:http://m.zhangshiyu.com/post/209755.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1