深入了解文件管理系统,初步掌握文件管理系统的实现方法。
用高级语言编写和调试一个简单的文件系统,模拟文件管理的工作过程。从而对各种文件操作命令的实质内容和执行过程有比较深入的了解。
编写一程序,模拟一个简单的文件管理系统。树型结构,目录下可以是目录,也可以是文件。
在此文件管理系统,可实现的操作有:
改变目录:格式:cd <目录名>
显示目录:格式:dir <目录名>
创建目录:格式:md <目录名>
删除目录:格式:rd <目录名>
新建文件:格式:edit <文件名>
删除文件:格式:del <文件名>
退出文件系统:exit
二.相关知识
1.文件结构体
struct FileNode
{
char filename[FILENAME_LEN];//文件名/目录名
int isdir;//目录文件识别标志
int i_nlink;//文件的链接数
int adr;//文件的地址
struct FileNode *parent, *child;//指向父亲的指针和指向左孩子的指针
struct FileNode *sibling_prev, *sibling_next;//指向前一个兄弟的指针和指向
//后一个兄弟的指针.
};
整个文件系统采用二叉树型存储结构,初始化文件树如下:
图 2-1 初始目录树
2.所使用函数及其功能
int Main(); //主函数
void Init();//初始化文件树
int ParseCommand();//接受输入的命令并把其分解成操作名和路径文件名
void ExecuteCommand();//执行命令,分别执行cd,edit,md,del,rd, dir,exit命令
int cdComd(); //改变目录功能处理
int editComd();//处理edit命令,即创建文件,只要创建表示文件的节点即可,内容及大小不考虑
int mdComd(); //创建目录
int delComd();//处理del命令,即删除指定文件,不存在是给出错误信息
int dirComd();//处理dir命令,显示目录
int rdComd(); //删除目录
int FindFilename(char Para2[]);//查找文件名
struct FileNode* CreateFileNode(char filename[],int isdir,int i_nlink);//创建结点
int GetInput(char* buffer,unsigned int buffer_len);//获取输入
3.所使用的变量
struct FileNode *cp, *tp, *root;// *cp, *tp, *root是根目录节点
char path[INPUT_LEN-COMMAND_LEN];//记录当前走过的路径
char Para1[COMMAND_LEN],Para2[INPUT_LEN-COMMAND_LEN];//para1数组存储输入的命令,para2数组存储输入的文件名
char filename[FILENAME_LEN],tmp;
unsigned int i,j;
三 题目分析
1.文件系统采用二叉树型存储结构,结点结构如下:
struct FileNode
{
char filename[FILENAME_LEN];//文件名/目录名
int isdir;//目录、文件的识别标志(0为文件,1为目录)
int i_nlink;//文件的链接数
//int adr;//文件的地址
struct FileNode *parent, *child;//指向父亲的指针和指向左孩子的指针
struct FileNode *sibling_prev, *sibling_next;//指向前一个兄弟的指针和指向后一个兄弟的指针.
};
2.目录名和文件名支持全路径名和相对路径名,路径名各分量间用“/”隔开
3.功能具体描述:
改变目录:改变当前工作目录,目录不存在时给出出错信息
显示目录:显示指定目录下或当前目录下所有文件和一级目录(选做:带/s参数的dir命令,显示所有子目录)
创建目录:在指定路径或当前路径下创建指定目录。重名时给出错信息。
删除目录:删除指定目录下所有文件和子目录。要删目录不空时,要给出提示是否要删除。
创建文件:创建指定名字的文件,只要创建表示文件的节点即可,内容及大小不考虑。
删除文件:删除指定文件,不存在时给出出错信息。
退出文件系统:exit
4、总体流程:
初始化文件目录;
输出提示符,等待接受命令,分析键入的命令;
对合法的命令,执行相应的处理程序,否则输出错误信息,继续等待新命令,直到键入EXIT退出为止。
四.概要设计
1.在内存中开辟一个虚拟磁盘空间作为文件存储器,在其上实现一个简单的单用户文件系统。
2.文件存储空间的分配采用显式链接分配。为了实现创建和删除文件必须要有一棵初始的文件树存在,以便在文件树的根节点下实现创建和删除文件。
3. 数据结构与树结构。数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。
树是一种重要的非线性数据结构,直观地看,它是数据元素(在树中称为结点)按分支关系组织起来的结构,很象自然界中的树那样。树中每个分叉点称为结点,起始结点称为树根,任意两个结点间的连接关系称为树枝,结点下面不再有分枝称为树叶。结点的前趋结点称为该结点的"双亲",结点的后趋结点称为该结点的"孩子",同一结点的"孩子"之间互称"兄弟"。
4.文件目录结构采用多级目录结构。为了简单起见,可以使用文件结构体,结构体内容包括:文件名,文件目录识别标示,文件链接数,以及他的左孩子右孩子左兄弟右兄弟指
5. 要有分解函数对输入的命令进行分解。以识别那部分是哪部分是命令,哪部分是路径和文件名。
6. 最后要有执行函数。来执行输入的创建文件命令。
五.代码及流程
图5-1 主函数流程图
2)edit()创建文件函数流程图
图5-2 创建文件函数流程图
图5-3 删除函数流程图
4)ParseCommand()分解命令函数流程图
图5-4 分解命令函数流程图
5)改变目录函数rdComd()
图 5-5 改变目录函数流程图
源代码:
#include  #include  #include  #include  #include  #define FILENAME_LEN 21 #define INPUT_LEN 81 #define COMMAND_LEN 11 using namespace std; void Init();//初始化文件树 int ParseCommand();//接受输入的命令并把其分解成操作名和路径文件名 void ExecuteCommand();//执行命令 int cdComd();//处理cd命令 int editComd();//处理edit命令 int delComd();//处理del命令 int dirComd();//处理dir命令 int mdComd();//处理md命令 int rdComd();//处理rd命令 int FindPath(char *ph);//寻找参数ph所指向的路径 int FindFilename(char Para2[]);//从参数Para2中找到要建立或删除的文件、目录名,并把指针只想其父亲结点 struct FileNode* CreateFileNode(char filename[],int isdir,int i_nlink);//创建结点 int GetInput(char* buffer,unsigned int buffer_len);//获取输入 int CheckCommand();//命令检查 int GetDir(int begin,char *path,char *curDir);//获取路径 struct FileNode *cp, *tp, *root; char path[INPUT_LEN-COMMAND_LEN];//记录当前走过的路径 char Para1[COMMAND_LEN],Para2[INPUT_LEN-COMMAND_LEN]; char curpath[INPUT_LEN-COMMAND_LEN],tmppath[INPUT_LEN-COMMAND_LEN]; char filename[FILENAME_LEN],tmp; unsigned int i,j;//int i,j; struct FileNode //结点结构 {     char filename[FILENAME_LEN];//文件名/目录名     int isdir;//目录文件识别标志     int i_nlink;//文件的链接数     struct FileNode *parent, *child;//指向父亲的指针和指向左孩子的指针     struct FileNode *sibling_prev, *sibling_next;//指向前一个兄弟的指针和指向后一个兄弟的指针. }; //创建结点 struct FileNode* CreateFileNode(char filename[],int isdir,int i_nlink)  {     struct FileNode* node=(struct FileNode*)malloc(sizeof(struct FileNode));//申请结点空间     //相应内容赋初值     strcpy(node->filename,filename);     node->isdir=isdir;     node->i_nlink=i_nlink;     node->parent=NULL;     node->child=NULL;     node->sibling_prev=NULL;     node->sibling_next=NULL;     return node; } //初始化文件树 void Init() {     struct FileNode *binNode,*usrNode,         *unixNode,*etcNode,*libNode,*userNode,         *binNode2,*liuNode,*sunNode,*ftiNode;     strcpy(path,"/"); //根目录写入当前路径     //创建文件树的结点     binNode=CreateFileNode("bin",1,0);     usrNode=CreateFileNode("usr",1,0);     unixNode=CreateFileNode("unix",0,0);     etcNode=CreateFileNode("etc",1,0);     libNode=CreateFileNode("lib",1,0);     userNode=CreateFileNode("user",1,0);     binNode2=CreateFileNode("bin",1,0);     liuNode=CreateFileNode("liu",1,0);     sunNode=CreateFileNode("sun",1,0);     ftiNode=CreateFileNode("fti",1,0);     cp=tp=root=CreateFileNode("/",1,0);     //结点相应内容赋值     root->parent=NULL;     root->child=binNode;     root->sibling_prev=root->sibling_next=NULL;          binNode->parent=root;     binNode->child=NULL;     binNode->sibling_prev=NULL;     binNode->sibling_next=usrNode;          usrNode->parent=NULL;     usrNode->child=libNode;     usrNode->sibling_prev=binNode;     usrNode->sibling_next=unixNode;          unixNode->parent=NULL;     unixNode->child=NULL;     unixNode->sibling_prev=usrNode;     unixNode->sibling_next=etcNode;          etcNode->parent=NULL;     etcNode->child=NULL;     etcNode->sibling_prev=unixNode;     etcNode->sibling_next=NULL;          libNode->parent=usrNode;     libNode->child=liuNode;     libNode->sibling_prev=NULL;     libNode->sibling_next=userNode;          userNode->parent=NULL;     userNode->child=NULL;     userNode->sibling_prev=libNode;     userNode->sibling_next=binNode2;          binNode2->parent=NULL;     binNode2->child=NULL;     binNode2->sibling_prev=userNode;     binNode2->sibling_next=NULL;          liuNode->parent=libNode;     liuNode->child=NULL;     liuNode->sibling_prev=NULL;     liuNode->sibling_next=sunNode;          sunNode->parent=NULL;     sunNode->child=NULL;     sunNode->sibling_prev=liuNode;     sunNode->sibling_next=ftiNode;          ftiNode->parent=NULL;     ftiNode->child=NULL;     ftiNode->sibling_prev=sunNode;     ftiNode->sibling_next=NULL;     } //获取文件或目录名,并把指针指向其父亲结点 int FindFilename(char Para2[])  {     i=strlen(Para2)-1;     j=0;         while(Para2[i]!='/'&& i>=0)    {             filename[j]=Para2[i];         i--; j++;     }     filename[j]='\\0';//获得逆序的文件或目录名,存入filename中     if(i<0) Para2[i+1]='\\0';     else Para2[i]='\\0';     j--;         for(i=0;i         filename[i]=filename[j];         filename[j]=tmp;     }         if(strlen(Para2)>0) {  //查找路径         int sign=FindPath(Para2);         if(sign==0)              return 0;     }     return 1; } //缓冲区安全输入子函数 //如果输入超过buffer_len,则截取前buffer_len-1长度的输入, //buffer_len处字符用'/0'代替 int GetInput(char* buffer,unsigned int buffer_len) {     unsigned int count=0;     while(count             buffer[count]='\\0';             return count;         }         count++;     }     while(getchar()!=10);     buffer[buffer_len-1]='\\0';     return -1; }     //改变目录函数 int cdComd() {     if(!CheckCommand()) //命令检查         return 0;     if(strcmp(Para2,"..")==0) {   //对cd..命令的处理         int i;         while(cp->sibling_prev)             cp=cp->sibling_prev;         if(cp->parent)         { cp=cp->parent; } //找到父亲结点         else         { return 0; }         //对当前路径进行相应处理         i=strlen(path);         while(path[i]!='/'&&i>0) i--;         if(i!=0)             path[i]='\\0';         else             path[i+1]='\\0';     }     else {         FindPath(Para2);//查找路径     }     return 1; } //创建目录函数 int mdComd() {          struct FileNode * temp,*tp;     temp=CreateFileNode("",1,0);     int sign;             if(strlen(Para2)==0) {  //参数不能为空         printf("\\n命令格式有错误.\\n");         return 0;     }         if(strlen(Para2)>20) {   //长度检查         printf("\\n目录名过长\\n");         return 0;     }     //格式检查     if (!(isalpha(Para2[0])||Para2[0]=='_'||Para2[0]=='\\0'||Para2[0]=='/'))    {         printf("目录名格式有错!\\n");/* 目录首字母可以为'字母'或'数字'或'/'*/         return 0;     }         sign=FindFilename(Para2);    //获取目录名     if(sign==0)         return 0;         if(cp->isdir!=1) {  //如当前指针指向的是文件,则报错         printf("you cannot edit a directory in under a file!\\n");         return 0;     }             tp=CreateFileNode(filename,1,0);  //创建目录结点,并插入到指定目录下         if(cp->child==NULL)    {             tp->parent=cp;         tp->child=NULL;         cp->child=tp;         tp->sibling_prev=NULL;         tp->sibling_next=NULL;     }     else {            temp=cp;//用temp找到新结点插入处               temp=temp->child;        while(temp->sibling_next ) {  //find the last sibing node            temp=temp->sibling_next;            if(strcmp(temp->filename,filename)==0&&temp->isdir==1) {             printf("此目录名已存在\\n");//重名报错             return 0;            }        }//找到了最后一个结点     temp->sibling_next=tp;     tp->parent=NULL;     tp->child=NULL;     tp->sibling_prev=temp;     tp->sibling_next=NULL;         }         return 1; } //删除目录函数 int rdComd() {      int sign;     struct FileNode *temp;     char cmd[2];         if(!CheckCommand()) //命令检查         return 0;         sign=FindFilename(Para2); //获取目录名     if(sign==0) return 0;             if(cp->child) {  //用temp指向要删除的结点         temp=cp->child;         while(temp->sibling_next && (strcmp(temp->filename,filename)!=0 || temp->isdir!=1))             temp=temp->sibling_next;         if(strcmp(temp->filename,filename)!=0) {             printf("不存在该目录!\\n");             return 0;         }     }     else {         printf("不存在该目录!\\n");         return 0;     }         if(temp->isdir!=1) {   //要删除的不能是文件         printf("ERROR!该命令只能删除目录,不可删除文件!\\n");         return 0;     }         if(temp->child)  {   //如仍有用户使用该目录,则不能删除         printf("\\n该目录不为空,您确定要删除吗?Y/N!\\n");         GetInput(cmd,2);         if(strcmp(cmd,"n")==0||strcmp(cmd,"N")==0)             return 0;     }     //删除工作     if(temp->parent==NULL) {   //不是第一个孩子         temp->sibling_prev->sibling_next=temp->sibling_next;         if(temp->sibling_next)//处理是最后一个兄弟的情况             temp->sibling_next->sibling_prev=temp->sibling_prev;             temp->sibling_prev=temp->sibling_next=NULL;     }//if     else { //第一个孩子         if(temp->sibling_next)//处理是最后一个兄弟的情况             temp->sibling_next->parent=temp->parent;             temp->parent->child=temp->sibling_next;     }     free(temp);         return 1; } //显示目录子函数 int dirComd() {     if(strlen(Para2)>0)    {         int sign=FindPath(Para2); //查找路径         if(sign==0)     {    return 0; }         else         {    printf("\\n%s>", path);    }     }         if(cp!=root)         printf("        if(cp->child==NULL) //指定目录为空         {    return 0;  }         tp=cp;         tp=tp->child;  //指定目录不为空,显示其所有子目录及文件名     while(tp) {         if(tp->isdir)             printf("            else             printf("            tp=tp->sibling_next;     }     return 0; } //创建文件函数 int editComd() {          struct FileNode * temp=CreateFileNode("",0,0);     int sign;     struct FileNode *tp;             if(strlen(Para2)==0) {  //路径不能为空         printf("\\n命令格式有错误.\\n");         return 0;     }         if(strlen(Para2)>20) {   //长度检查         printf("\\n文件名过长\\n");         return 0;     }     //格式检查     if (!(isalpha(Para2[0])||Para2[0]=='_'||Para2[0]=='\\0'||Para2[0]=='/'))    {         printf("文件名格式有错!\\n");/* 文件首字母可以为'字母'或'数字'或'_'或'/'或'回车'*/         return 0;     }             sign=FindFilename(Para2);//获取文件名     if(sign==0)         return 0;         if(cp->isdir!=1) {   //如当前指针指向的是文件,则报错         printf("you cannot edit a file in under a file!\\n");         return 0;     }         //创建文件结点,并插入到指定目录下     tp=CreateFileNode("",1,0);     strcpy(tp->filename,filename);     tp->isdir=0;     tp->i_nlink=0;     if(cp->child==NULL)    {             tp->parent=cp;         tp->child=NULL;         cp->child=tp;         tp->sibling_prev=NULL;         tp->sibling_next=NULL;     }     else {             temp=cp;                temp=temp->child;//用temp找到新结点插入处         while(temp->sibling_next ) {  //find the last sibing node             temp=temp->sibling_next;             if(strcmp(temp->filename,filename)==0&&temp->isdir==0) {                 printf("此文件名已存在,请重新输入\\n"); //重名报错                 return 0;             }         }//找到了最后一个结点     temp->sibling_next=tp;     tp->parent=NULL;     tp->child=NULL;     tp->sibling_prev=temp;     tp->sibling_next=NULL;         }         return 1; } //删除文件子函数 int delComd() {      int sign;     struct FileNode *temp;         if(strlen(Para2)==0) {   //参数不能为空         printf("\\n命令格式有错误.\\n");         return 0;     }     sign=FindFilename(Para2);   //获取文件名     if(sign==0) return 0;             if(cp->child) {   //用temp指向要删除的结点         temp=cp->child;         while(temp->sibling_next&&(strcmp(temp->filename,filename)!=0||temp->isdir!=0))             temp=temp->sibling_next;         if(strcmp(temp->filename,filename)!=0) {             printf("不存在该文件!\\n");             return 0;         }     }     else {         printf("不存在该文件!\\n");         return 0;     }         if(temp->isdir!=0) {   //要删除的不能是目录         printf("ERROR!该命令只能删除文件,不可删除目录!\\n");         return 0;     }         if(temp->i_nlink!=0) {   //如仍有用户使用该文件,则不能删除         printf("还有用户共享了该文件,不能删除!\\n");         return 0;     }     //删除工作     if(temp->parent==NULL) {   //不是第一个孩子         temp->sibling_prev->sibling_next=temp->sibling_next;         if(temp->sibling_next)//处理是最后一个兄弟的情况             temp->sibling_next->sibling_prev=temp->sibling_prev;             temp->sibling_prev=temp->sibling_next=NULL;     }     else { //第一个孩子         if(temp->sibling_next)//处理是最后一个兄弟的情况             temp->sibling_next->parent=temp->parent;             temp->parent->child=temp->sibling_next;     }     free(temp);         return 1; } //获取当前目录名子函数 int GetDir(int begin,char *path,char *curDir) {     int i=0;     int len=strlen(path);     while(!((path[begin]=='\\\\')||(path[begin]=='/'))&&begin     curDir[i]='\\0';     return begin+1; } //查找路径函数 int FindPath(char *ph) {     struct FileNode *temp; //struct FileNode *tp,*temp;     char oldpath[INPUT_LEN-COMMAND_LEN];     unsigned int i=0; //int i=0     int sign=1;     if(strcmp(ph,"/")==0) {     //ph是根目录         cp=root;         strcpy(path,"/");         return 1;     }     temp=cp;     strcpy(oldpath,path);//保留原路径和指针     if(ph[0]=='/')    {    //指针指向根目录的左孩子         cp=root->child;         i++; //滤过'/'         strcpy(path,"/");     }     else {         if(cp!=NULL&&cp!=root)             strcat(path,"/");         if(cp&&cp->child) {             if(cp->isdir)                 cp=cp->child;//指针指向当前目录的左孩子             else {                 printf("路径错误!\\n");                 return 0;             }         }     }     while(i<=strlen(ph)&&cp) {  //继续查找指定路径,如遇到文件则报错         int j=0;         if(ph[i]=='/'&&cp->child) {             i++; //略过'/'             if(cp->isdir)                 cp=cp->child; //继续查找下级目录             else  {                 printf("路径错误!\\n");                 return 0;             }             strcat(path,"/");         }           while(ph[i]!='/'&&i<=strlen(ph)) { // curpath 记录当前要找的路径名             curpath[j]=ph[i];             i++; j++;         }         curpath[j]='\\0';         while((strcmp(cp->filename,curpath)!=0||(cp->isdir!=1))&&cp->sibling_next!=NULL)         {  cp=cp->sibling_next;  }         if(strcmp(cp->filename,curpath)==0)    {             if(cp->isdir==0) {                 strcpy(path,oldpath);                 cp=temp;                 printf("是文件不是目录.\\n");                 return 0;             }             strcat(path,cp->filename);         }         if(strcmp(cp->filename,curpath)!=0||cp==NULL) {             strcpy(path,oldpath);             cp=temp;             printf("输入路径错误\\n");             return 0;         }     }     return 1; } //命令检查函数 int CheckCommand() {     if(strlen(Para2)==0) {         printf("命令语法不正确.\\n");         return 0;     }     return 1; } //分解命令子函数 int ParseCommand() {     char Inputs[INPUT_LEN];     int i=0,j=0,ch;     unsigned int k=0;     printf("%s>",path);         if(GetInput(Inputs,INPUT_LEN)==-1) {  //获取输入         printf("输入行太长.\\n");         return 0;     }     Para1[0]=Para2[0]='\\0';     //获取参数Para1,即操作名     while(Inputs[i]!=' '&&Inputs[i]!='\\0'&&i         i++;     }     Para1[i]='\\0';             if(i==(COMMAND_LEN-1)) return 1;  //输入命令太长             if(Inputs[i]!='\\0') {    //获取参数2,即路径文件名         while(Inputs[i]==' '&&i             i++; j++;         }         Para2[j]='\\0';     }             for(k=0;k         Para1[k]=ch;     }     return 1; } //执行命令函数 void ExecuteCommand() {     int sign;     //根据参数Para1调用相应的功能处理模块     if(strcmp(Para1,"cd")==0)             sign=cdComd();   //cd命令     else if(strcmp(Para1,"edit")==0)          sign=editComd();  //edit命令     else if(strcmp(Para1,"md")==0)         sign=mdComd();    //     else if(strcmp(Para1,"del")==0)          sign=delComd();   //del命令     else if(strcmp(Para1,"rd")==0)         sign=rdComd();     else if(strcmp(Para1,"dir")==0)          sign=dirComd();   //dir命令     else if(strcmp(Para1,"exit")==0)         exit(0);                    //exit命令     else         printf("命令错误,请重试\\n");  //命令输入不正确,报错 } int main() {     Init(); //初始化文件树     while(1) {         if(ParseCommand())     //分解命令             ExecuteCommand();  //执行命令     }     return 0; } 六.运行结果 1.显示根目录下所有文件和目录 图 6-1 当前根目录下目录和文件 2.创建目录和文件 图 6-2创建目录和文件 3.删除目录和文件 图 6-3 删除目录和文件 4.创建文件重名情况 图 6-4 创建文件重名的情况 5.改变目录 图 6-5 改变目录 6.退出系统 图 6-6 退出文件系统 八.参考文献 [1] 操作系统. 宗大华 宗涛 陈吉人 编著.北京:人民邮电出版社,2009. [2] 数据结构(C语言版). 严蔚敏 吴伟民 编著.北京:清华大学出版社,2006. [3] C语言程序设计. 马秀丽 刘志妩 李筠 编著.北京:清华大学出版社,2008. 下载本文