一、 程序介绍
用C++编写出一个简单的模拟文件系统,实现目录的添加、删除、重命名,文件的添加、删除、重命名、文件和目录、文件的拷贝。
二、 程序内容与要求
1) 设计和实现一个简单的文件系统,要求包括目录、普通文件和文件的存储
2) 按文件系统的目录结构采用类似Linux的树状结构;
3) 要求模拟的操作包括:
a) 目录的添加、删除、重命名;
b) 目录的显示(列表)
c) 文件的添加、删除、重命名
d) 文件和目录的拷贝
4) 用户进入时显示可用命令列表;用户输入help时显示所有命令的帮助文档;输入某个命令+?时显示该条命令的使用说明
5) 用户输入exit时退出该系统
6) 程序实现基于windows平台;
7) 程序开发语言选用C/c++
三、 实验设计
3.1 系统流程如图1
图1 系统流程
3.2 树状结构
我设计了一个根目录仅记录为WP:,其中树形图通过父节点和子节点双向连接,父节点和其子节点的兄弟节点单向连接,子节点和其兄弟节点单向连接构成。
图2 树状结构
3.3 实现命令操作
1. echo命令实现:echo命令可以在当前目录下创建一个文件
1) 判断创建文件与已知文件、目录是否有重名现象,在当前目录下文件名不重复且有空闲物理块时创建文件节点
2) 判断当前目录下节点情况,在链表末尾连上新建立节点,并输出对应文件创建成功。
3) 流程图如图3
图3 echo创建文件
2. md命令实现:md命令可以在当前目录下创建一个目录文件
1) 判断创建目录与已知文件、目录是否有重名现象,在当前目录下目录名不重复且有空闲物理块时创建目录节点
2) 判断当前目录下节点情况,在链表末尾连上新建立节点,并输出对应目录创建成功。
3) 流程图如图4
图4 md创建目录
3. cd命令实现:cd命令可以进入下一级目录或返回上级目录、根目录
1) 判断输入内容,当输入.时返回上级目录,输入..时返回根目录。
2) 输入具体目录名时判断当前父目录下子节点情况,调用函数搜索当前父目录下子节点是否存在对应目录名且节点类型为目录的节点,当存在该目录时进入该目录,否则输出当前目录下不存在该下一级目录。
3) 流程图如图5
图5 cd进入对应目录
4. del命令实现:del命令可以在当前目录下删除一个已存在的文件
1) 判断输入内容是否存在要删除的文件。
2) 通过遍历找到对于文件名的文件节点。
3) 将所占用物理块内容赋值为-99,清空文件内容,通过上一个节点指向NULL将该节点从链表中移除,释放该文件节点。
4) 流程图如图6
图6 del命令
5. dir命令实现:dir命令可以显示当前目录下的所有文件和目录
1) 判断当前目录下子节点是否为空。
2) 设计节点变量记录首个子节点,判断变量类型,如果是文件节点输出类型,文件名和文件内容,如果是目录节点输出类型,目录名。
3) 判断当前目录下剩余节点子节点是否为空,不为空时指向下一个节点重复输出节点内容。
4) 流程图如图7
图7 dir命令
6. mem命令实现:men命令可以查看模拟物理盘块的使用情况
1) 判断当前目录下子节点是否为空。
2) 设计for循环输出所有物理块占用情况,其中-99表示未占用,-3表示占用。
7. wrt命令实现:wrt命令可以写入当前目录下一个文件的内容
1) 判断输入内容是否正确
2) 判断当前目录下是否存在对应文件节点。
3) 遍历当前目录子节点,找到该节点后记录输入文件内容至字符串数值。并显示写入成功否则显示写入失败
4) 流程图如图8
图8 wrt命令
8. typ命令实现:typ命令可以读取当前目录下一个文件的内容
1) 判断输入内容是否正确
2) 判断当前目录下是否存在对应文件节点。
3) 遍历当前目录子节点,找到该节点后输出类型、文件名、所占物理块、文件内容。并显示读取成功否则显示读取失败
4) 流程图如图9
图9 typ命令
9. ren命令实现:ren命令可以重命名一个存在于当前目录下的文件或目录
1) 判断输入内容是否正确
2) 判断当前目录下是否存在对应文件或目录节点。
3) 遍历当前目录子节点,找到该节点后将其名称设置为输入内容

图10 ren命令
10. cp命令实现:cp命令可以复制当前目录下一个指定的文件或目录至指定的目录
1) 判断输入内容是否正确
2) 判断当前目录下是否存在对应文件或目录节点。
a) 文件复制和目录节点无子节点复制情况:首先找到指定目录;然后判断该目录下节点情况来确认复制后新节点的位置,然后建立新节点其内容为复制节点内容并将其连接至确定好的位置。
b) 级联复制:首先复制首个子节点,然后设置while(1)循环进入子节点的子节点进行复制,碰到有子节点的节点就进入其子节点复制,直到复制到指向兄弟指针为NULL时返回上一层,设置if判断语句判断当前复制的父节点是否为首个目录节点父节点,若满足条件说明此时都复制完毕。

图11 cp命令
11. der命令实现:der命令可以在当前目录下删除一个已存在的目录
1) 判断输入内容是否正确
2) 判断当前目录下是为单一目录节点若是则直接删除。
3) 遍历删除情况:首先进入子节点然后设置while循环横向移动指针知道兄弟节点为NULL,确保现在指向指针为待删除目录最右下的节点,删除该节点然后重复while循环确保每次删除都为最右下的节点,目录删除完毕。

图12 der命令
四、 数据结构
文件、目录数据结构:
class fnode
{
public:
string filename; /文件名
int type; //文件类型,1为普通文件,0为目录文件
fnode *parent; //父节点
fnode *child; //子节点
fnode *brother; //兄弟节点
int block_num; //表示占用的物理盘块号
int isBro; //表示是否是在同级目录下,0表示不是,1表示是
int isCld; //是否为目录下第一个节点,1表示是,0表示不是
fnode();
fnode(string n)
fnode(string n, int t, int b)
fnode(fnode *p, string name, int t, int b)
void setisCld()
};
#include <string>
#include <cstring>
#include"标头.h"
#include"stdio.h"
#include <windows.h>
#include <iostream>
using namespace std;
int k = 1; //记录下一次操作的节点号
//int k1 = 0; //临时记录当前节点号
fnode *fnode1[1024];
fnode *parent1; //当前目录下父节点
//fnode *root = new fnode("WP:", 0, 0); //设置根目录
fnode *current_path=new fnode();
int seeknum(fnode *fnode2);
void freeblock(fnode *fnode0, fnode *fnode2) //释放物理盘块参数1为左兄弟,参数2为释放节点
{
//cout << "B0" << endl;
int i = seeknum(fnode2);
if (fnode2->isCld == 1)
{
//cout << "B10" << endl;
if (fnode2->brother == NULL)
{
//cout << "B1" << endl;
fnode2->parent->child = NULL;//删除目录为第一子节点且无其他子节点
//cout << "B1" << endl;
}
else
{
fnode2->parent->child = fnode2->brother;
}
}
else
{
if (fnode2->brother != NULL)//同级节点有左右兄弟节点
{
fnode0->brother = fnode2->brother;
}
else//同级节点无右兄弟节点
{
fnode0->brother = NULL;
}
}
//cout << "B11" << endl;
block[fnode2->block_num] = -99;//删除所占用物理盘快
if (fnode2->type == 1)
{
content[fnode2->block_num] = "";
}
fnode*fnode7 = fnode1[i];
//cout << "B12" << endl;
fnode1[i] = NULL;
delete fnode7;
}
int seeknum(fnode *fnode2)
{
int i;
for (i = 0; i < k; ) //检索所有节点找到节点号
{
if (fnode1[i] != NULL)
{
if (fnode1[i]->type == fnode2->type & fnode1[i]->parent == fnode2->parent&fnode1[i]->filename == fnode2->filename)
{
//k1 = i;
return i;
}
}
i++;
}
if (i == k)
{
return 1025;
}
}
int repeatname(string s)
{
int i;
if (parent1->child != NULL)
{
for (i = 1; i < k; ) //检索所有节点判断当前目录下文件是否重名
{
if (fnode1[i]!= NULL)
{
if (fnode1[i]->type == 1 & fnode1[i]->parent == parent1&fnode1[i]->filename == s)
{
return 1;//文件名有重复
}
else if (fnode1[i]->type == 0 & fnode1[i]->parent == parent1&fnode1[i]->filename == s)
{
return 2;//目录名有重复
}
}
i++;
}
}
//if (i == k) return 0;
return 0;
}
void show_current_path() //显示当前路径
{
fnode*fnode2=parent1; //临时节点记录当前目录
//current_path->filename = fnode2->filename; ?只对文件名复制会产生错误 ?
string s= parent1->filename;
if (fnode2 == fnode1[0]) //当前目录为根目录时
{
s = fnode1[0]->filename;
current_path = new fnode(s);//不能直接更改文件名
return;
}
else
{
if (fnode2->parent == fnode1[0])
{
s = fnode2->parent->filename + '\\' + s;
current_path = new fnode(s);//不能直接更改文件名
return;
}
else
{
while (fnode2->parent != fnode1[0])
{
s = fnode2->parent->filename + '\\' + s;
fnode2 = fnode2->parent;
}
if (fnode2->parent == fnode1[0])
{
s = fnode2->parent->filename + '\\' + s;
current_path = new fnode(s);//不能直接更改文件名
return;
}
}
}
// delete fnode2;
}
int display()
{
string s1, s2;
//s1 = "许鑫博";
//s2 = "101";
cout << "/************************************************************************/" << endl;
cout << "/******************文件系统设计******************************************/" << endl;
cout << "/* 命令格式 说明 **/" << endl;
cout << "/* echo+name 创建文件 **/" << endl;
cout << "/* md+name 创建目录 **/" << endl;
cout << "/* del+name 删除文件 **/" << endl;
cout << "/* der+name 删除目录 **/" << endl;
cout << "/* cd+name 进入目录 **/" << endl;
cout << "/* cp+name+road 复制文件或目录 **/" << endl;
cout << "/* dir 查看目录下内容 **/" << endl;
cout << "/* ren+name+newname 重命名文件或目录 **/" << endl;
cout << "/* wrt+name+content 写入文件内容 **/" << endl;
cout << "/* mem 查看物理磁盘 **/" << endl;
cout << "/* help 查看帮助 **/" << endl;
cout << "/* 命令+? 查看命令作用 **/" << endl;
cout << "/* exit 退出 **/" << endl;
cout << "/* cls 清屏 **/" << endl;
cout << "/************************************************************************/" << endl;
cout << "/************************************************************************/" << endl;
while (1)
{
cout << "请输入用户名:";
getline(cin, s1);
cout << "请输入密码:";
getline(cin, s2);
if (s1 == "许鑫博"&s2 == "101")
{
break;
}
else
{
cout << "用户名或密码错误请重试:" << endl;
}
}
if (s1 == "许鑫博"&s2 == "101")
{
cout << "欢迎进入简单文件系统:" << endl;
return 0;
}
else
{
return 1;
}
}
int main()
{
//while (display()){}
initialize();//初始化
int Msize = 5; //此处为模拟磁盘大小为输入5个字符
fnode1[0] = new fnode("WP:", 0, 0);//设置根目录
string str;
parent1 = fnode1[0]; //记录当前目录
/*int i;
for (i = 0; i < block_size; i++)
{
cout << block[i] << " ";
}
cout << endl;*/
cout << parent1->filename << ">";
getline(cin, str);//输入字符串以回车结束
current_path = parent1; //记录当前路径
while (1)
{
if (str.compare("exit") != 0)
{
if (str.compare("help") == 0)
{
help();
}
else if (str.substr(0, 4).compare("echo") == 0)
{
//echo命令可以在当前目录下创建一个文件
if (str.substr(4).compare("?") == 0)
{
cout << "echo命令可以在当前目录下创建一个文件,格式为echo+name" << endl;
}
else if (str.substr(4) == "")
{
cout << "name命名错误" << endl;
}
else if (repeatname(str.substr(5)) == 2)
{
cout << "当前目录下目录与文件名有重复" << endl;
}
else if (repeatname(str.substr(5)) == 1)
{
cout << "当前目录下文件名有重复" << endl;
}
else
{
//当前目录下文件名不重复且有空闲物理块时创建
if (repeatname(str.substr(5)) != 1 & seekfreeblock() != 0)
{
fnode1[k] = new fnode(parent1, str.substr(5), 1, seekfreeblock());
if (parent1->child == NULL)//判断当前目录下子节点是否为空
{
parent1->child = fnode1[k];
fnode1[k]->setisCld();
}
else //判断当前指针指向节点的兄弟节点是否为空
{
fnode* fnode2 = parent1->child;
while (fnode2->brother != NULL)
{
fnode2 = fnode2->brother;
}
fnode2->brother = fnode1[k];
}
k++;
cout << fnode1[k-1]->filename;
cout << "文件创建成功" << endl;
}
}
}
else if (str.substr(0, 2).compare("md") == 0)
{
//md命令可以在当前目录下创建一个目录文件
if (str.substr(2).compare("?") == 0)
{
cout << "md命令可以在当前目录下创建一个目录文件,具体格式:md+name" << endl;
}
else if (repeatname(str.substr(3)) == 2)
{
cout << "当前目录下目录名有重复" << endl;
}
else if (repeatname(str.substr(3)) == 1)
{
cout << "当前目录下目录与文件名有重复" << endl;
}
else
{
//当前目录下目录名不重复且有空闲物理块时创建
if (repeatname(str.substr(3)) != 2 & seekfreeblock() != 0)
{
fnode1[k] = new fnode(parent1, str.substr(3), 0, seekfreeblock());
if (parent1->child == NULL)//判断当前目录下子节点是否为空
{
parent1->child = fnode1[k];
fnode1[k]->setisCld();
}
else //判断当前指针指向节点的兄弟节点是否为空
{
fnode* fnode2 = parent1->child;
while (fnode2->brother != NULL)
{
fnode2 = fnode2->brother;
}
fnode2->brother = fnode1[k];
}
k++;
cout << fnode1[k-1]->filename;
cout << "目录创建成功" << endl;
}
}
}
else if (str.substr(0, 2).compare("cd") == 0)
{
//cd命令可以进入下一级目录或返回上级目录、根目录
if (str.substr(2).compare("?") == 0)
{
cout << "cd命令可以进入下一级目录或返回上级目录、根目录,具体格式:cd+name或cd+.或cd+.." << endl;
}
else if (str.substr(2) == "")
{
cout << "name命名错误" << endl;
}
else if (str.substr(2).compare(".") == 0)
{
if (parent1->parent == NULL)
{
cout << "无法返回上级目录,已为根目录" << endl;
}
else
{
parent1 = parent1->parent;
show_current_path(); //查找当前上级目录
}
}
else if (str.substr(2).compare("..") == 0)
{
current_path = fnode1[0];//当前路径为根目录
parent1 = fnode1[0];
}
else
{
//当前目录下目录名是否存在
//cout << k <<" "<< repeatname(str.substr(3))<< endl;
if (repeatname(str.substr(3)) == 2 )
{
//cout << parent1->filename << endl;
fnode *fnode2 = parent1->child;
while (fnode2->filename != str.substr(3)| fnode2->type!=0)
{
fnode2 = fnode2->brother;
}
//cout << fnode2->filename << endl;
parent1 = fnode2;
show_current_path(); //查找当前目录
}
else if (repeatname(str.substr(3)) != 2)
{
cout <<"当前目录下不存在该下一级目录" << endl;
}
}
}
else if (str.substr(0, 3).compare("del") == 0)
{
//del命令可以在当前目录下删除一个已存在的文件
if (str.substr(3).compare("?") == 0)
{
cout << "del命令可以在当前目录下删除一个已存在的文件,具体格式:del+name" << endl;
}
else if (str.substr(3) == "")
{
cout << "name命名错误" << endl;
}
else
{
//cout <<"a"<< repeatname(str.substr(4)) << endl;
if (repeatname(str.substr(4)) == 1)
{
//cout << parent1->filename << endl;
if (parent1->child == NULL)
{
cout << "error" << endl;
}
else
{
fnode *fnode2 = parent1->child;
if (fnode2->filename == str.substr(4) & fnode2->type == 1)
{
block[fnode2->block_num] = -99;//删除所占用物理盘快
content[fnode2->block_num] = "";//删除对应文件内容
cout << "删除文件所占磁盘块号为" << fnode2->block_num << "; ";
if (fnode2->isCld == 1)
{
if (fnode2->brother == NULL)
{
fnode2->parent->child = NULL;//删除目录为第一子节点且无其他子节点
}
else
{
fnode2->parent->child = fnode2->brother;
fnode2->brother->setisCld();
//cout << fnode2->parent->child->filename <<endl;
}
}
//parent1->child = NULL;
fnode*fnode7 = fnode1[seeknum(fnode2)];
fnode1[seeknum(fnode2)] = NULL;
delete fnode7;
//k--;
cout << str.substr(4)<<"对应文件已删除" << endl;
}
else
{
fnode*fnode3 = fnode2;//临时节点记录前一个节点
while (fnode2->filename != str.substr(4) | fnode2->type != 1)//防止出现同名目录
{
fnode3 = fnode2;
fnode2 = fnode2->brother;
}
block[fnode2->block_num] = -99;//删除所占用物理盘快
content[fnode2->block_num] = "";//删除对应文件内容
cout << "删除文件所占磁盘块号为" << fnode2->block_num<<" ";
if (fnode2->isCld == 1)
{
if (fnode2->brother == NULL)
{
//cout << "B1" << endl;
fnode2->parent->child = NULL;//删除目录为第一子节点且无其他子节点
}
else
{
fnode2->parent->child = fnode2->brother;
}
}
else
{
if (fnode2->brother != NULL)//同级节点有左右兄弟节点
{
fnode3->brother = fnode2->brother;
}
else//同级节点无右兄弟节点
{
fnode3->brother = NULL;
}
}
//fnode3->brother = NULL;
fnode*fnode7 = fnode1[seeknum(fnode2)];
fnode1[seeknum(fnode2)] = NULL;
delete fnode7;
//k--;
cout << str.substr(4)<< "对应文件已删除" << endl;
}
}
}
}
}
/*修改*/
else if (str.substr(0, 3).compare("der") == 0)
{
//der命令可以在当前目录下删除一个已存在的目录
if (str.substr(3).compare("?") == 0)
{
cout << "der命令可以在当前目录下删除一个已存在的目录,具体格式:der+name" << endl;
}
else if (str.substr(3) == "")
{
cout << "name命名错误" << endl;
}
else if (repeatname(str.substr(4)) == 2)
{
//cout << parent1->filename << endl;
//cout << "b" << repeatname(str.substr(4)) << endl;
if (parent1->child == NULL)
{
cout << "error" << endl;
}
else
{
fnode *fnode2 = parent1->child;
fnode* fnode6 = fnode2;
while (fnode2->filename != str.substr(4) | fnode2->type != 0)
{
fnode6 = fnode2;
fnode2 = fnode2->brother;
}
if (fnode2->filename == str.substr(4) & fnode2->type == 0)
{
//cout << "a3" << endl;
if (fnode2->child == NULL)//要删除的目录下无文件或目录
{
//cout << "a4" << endl;
cout << fnode2->filename<< "目录已删除" << endl;
//cout << fnode2->isCld << endl;
freeblock(fnode6, fnode2);
//cout << "a5" << endl;
}
else//要删除的目录下有文件或目录
{
/*先从原目录移除在释放*/
if (fnode2->isCld == 1)
{
if (fnode2->brother == NULL)
{
fnode2->parent->child = NULL;//删除目录为第一子节点且无其他子节点
}
else
{
fnode2->parent->child = fnode2->brother;
fnode2->brother->setisCld();
}
}
else
{
if (fnode2->brother != NULL)//同级节点有左右兄弟节点
{
fnode6->brother = fnode2->brother;
}
else//同级节点无右兄弟节点
{
fnode6->brother = NULL;
}
}
//cout << "a1" << endl;
fnode*fnode4 = fnode2;//临时记录要删除父节点
fnode*fnode3 = fnode4->child;
fnode*fnode5 = fnode4->child;//临时记录兄弟节点
while (1)
{
//cout << "a0" << endl;
fnode5 = fnode4->child;
fnode3 = fnode4->child;
while (fnode3->brother != NULL)
{
fnode5 = fnode3;
fnode3 = fnode3->brother;
}
//cout << fnode5->filename << "a1 " << fnode3->filename << endl;
if (fnode3->type == 1)//普通文件无child
{
//cout << "删除文件" << fnode3->filename << endl;
block[fnode3->block_num] = -99;//删除所占用物理盘快
content[fnode3->block_num] = "";//删除对应文件内容
//fnode3->parent->child = NULL;
if (fnode3->isCld == 1)//删除对应节点为第一子节点
{
//cout << fnode5->filename << " " << fnode3->filename << endl;
//cout << fnode3->filename << endl;
fnode4->child = NULL;
//delete fnode1[seeknum(fnode3)];//删除对应节点
fnode*fnode7 = fnode1[seeknum(fnode3)];
fnode1[seeknum(fnode3)] = NULL;
delete fnode7;
//k--;
fnode3 = fnode4;//进入上一级
if (fnode3 == fnode1[0])
{
break;
}
else
{
fnode4 = fnode4->parent;
}
//cout << "进入上一级" << fnode3->filename << endl;
//cout << "进入上一级" << fnode4->child->filename << " " << fnode3->filename << endl;
if (fnode4 == fnode2->parent)
{
//cout << "v" << endl;
freeblock(fnode6, fnode3);
break;
}
}
else
{
fnode5->brother = NULL;//左节点的兄弟指针指向null
//cout << fnode5->filename << " " << fnode3->filename << endl;
//delete fnode1[seeknum(fnode3)];//删除对应节点
fnode*fnode7 = fnode1[seeknum(fnode3)];
fnode1[seeknum(fnode3)] = NULL;
delete fnode7;
//k--;
fnode3 = fnode4->child;
}
}
else if (fnode3->type == 0 & fnode3->child == NULL)//为目录文件且无子节点
{
//cout << "a3" << endl;
//cout << "删除目录文件且无子节点" << endl;
//cout << fnode5->filename << " " << fnode3->filename << endl;
//cout << fnode3->filename << endl;
block[fnode3->block_num] = -99;//删除所占用物理盘快
if (fnode3->isCld == 1)//删除对应节点为第一子节点
{
fnode4->child = NULL;
//delete fnode1[seeknum(fnode3)];//删除对应节点
fnode*fnode7 = fnode1[seeknum(fnode3)];
fnode1[seeknum(fnode3)] = NULL;
delete fnode7;
//k--;
fnode3 = fnode4;//进入上一级
fnode4 = fnode4->parent;
if (fnode4 == fnode2->parent)
{
freeblock(fnode6, fnode2);
break;
}
}
else
{
fnode5->brother = NULL;//左节点的兄弟指针指向null
fnode*fnode7 = fnode1[seeknum(fnode3)];
fnode1[seeknum(fnode3)] = NULL;
delete fnode7;
//k--;
//cout << "a3" << endl;
fnode3 = fnode4->child;
}
}
else//为目录文件且有子节点进入子节点删除
{
//cout << "进入目录文件删除" << endl;
fnode4 = fnode3;
fnode3 = fnode3->child;
}
}
cout << str.substr(4)<< "目录已删除" << endl;
}
}
}
}
else if (repeatname(str.substr(4)) != 2)
{
cout << "当前目录下不存在该目录,无法删除" << endl;
}
}
else if (str.substr(0, 2).compare("cp") == 0)
{
if (str.substr(2).compare("?") == 0)
{
cout << "cp命令可以复制当前目录下一个指定的文件或目录至指定的目录,具体格式:cp+name+road" << endl;
}
else
{
if (str.substr(2) == "")
{
cout << "name命名错误" << endl;
}
else if (str.find_last_of(' ')==2)
{
cout << "road命名错误" << endl;
}
else
{
if (repeatname(str.substr(3, str.find_last_of(' ')-3)) == 0)
{
cout << "当前目录下不存在该文件或目录,无法复制" << endl;
}
else if (repeatname(str.substr(3, str.find_last_of(' ') - 3)) != 0)
{
string current_path1 = str.substr(str.find_last_of(' ')+1);//记录指定路径
int i=current_path1.length();
int j1 = 0;//记录前一个斜杠位置+1
int j2 = 0;//记录后一个斜杠位置+1
string s = current_path1.substr(0, 3);
fnode*fnode2 = fnode1[0];
if (fnode2->filename != s)//先判断根目录
{
cout << "不存在该路径,无法复制" << endl;
}
else//根目录正确情况下寻找指定路径
{
i = i - 3;
j1 = j1 + fnode2->filename.length() + 1;
j2 = current_path1.find('\\', j1);
//substr()参数1为起始位置,参数2是截取长度
while (i != 0)
{
s= current_path1.substr(j1, j2-j1);
fnode2 = fnode2->child;
while (s != fnode2->filename&fnode2->brother != NULL)
{
fnode2 = fnode2->brother;
}
if (s != fnode2->filename&fnode2->brother == NULL)
{
cout << "不存在该路径,无法复制" << endl;
break;
}
else if (s == fnode2->filename)
{
i = i - s.length()-1;//1为斜杠'\'
j1 += s.length() + 1;
j2 = current_path1.find('\\', j1);
}
}
if (i == 0)
{
//cout << "当前该路径正确" << endl;
/*缺少内容的复制*/
if (repeatname(str.substr(3, str.find_last_of(' ') - 3)) == 1)
{
fnode1[k] = new fnode(fnode2, str.substr(3, str.find_last_of(' ') - 3), 1, seekfreeblock());
if (fnode2->child == NULL)//判断指定目录下子节点是否为空
{
fnode2->child = fnode1[k];
fnode1[k]->setisCld();
}
else //判断当前指针指向节点的兄弟节点是否为空
{
fnode* fnode3 = fnode2->child;
while (fnode3->brother != NULL)
{
fnode3 = fnode3->brother;
}
fnode3->brother = fnode1[k];
}
fnode*fnode3 = parent1->child;//被复制目录指针
while (fnode3->type != 1| fnode3->filename!= fnode1[k]->filename)
{
fnode3 = fnode3->brother;
//fnode3 = fnode3->brother;
}
content[fnode1[k]->block_num] = content[fnode3->block_num];
k++;
cout << fnode1[k - 1]->filename;
cout << "文件复制成功" << endl;
}
else//目录复制情况
{
fnode1[k] = new fnode(fnode2, str.substr(3, str.find_last_of(' ') - 3), 0,seekfreeblock());
if (fnode2->child == NULL)//确定要复制的根节点位置判断指定目录下子节点是否为空
{
fnode2->child = fnode1[k];
fnode1[k]->setisCld();
}
else //判断当前指针指向节点的兄弟节点是否为空
{
fnode* fnode3 = fnode2->child;
while (fnode3->brother != NULL)
{
fnode3 = fnode3->brother;
}
fnode3->brother = fnode1[k];
}
fnode*fnode4 = fnode1[k];//指向复制后根位置指针
k++;
fnode*fnode3 = parent1->child;//被复制目录指针
while (fnode3->type != 0 | fnode3->filename != str.substr(3, str.find_last_of(' ') - 3))
{
fnode3 = fnode3->brother;
}
fnode*fnode5 = fnode3;//临时指针被复制目录指针
fnode* fnode6 = fnode4;//临时指针指向待复制位置左兄弟指针
fnode*fnode7 = fnode4;//临时指针指向待复制位置父指针
/*头节点复制完毕*/
if (fnode5->child != NULL)
{
fnode5 = fnode5->child;
while (1)
{
//fnode7 = fnode7->child;
fnode1[k] = new fnode(fnode7, fnode5->filename, fnode5->type, seekfreeblock());
//cout << fnode7->filename<<" "<<fnode1[k]->filename << endl;
if (fnode5->isCld == 1)
{
fnode1[k]->setisCld();
fnode7->child = fnode1[k];
fnode6 = fnode1[k];
//cout << fnode7->filename << " 1" << fnode1[k]->filename << endl;
}
else
{
fnode6->brother = fnode1[k];
//cout << fnode6->filename << "2 " << fnode1[k]->filename << endl;
}
k++;
if (fnode5->type == 1)
{
/*缺少fnode1[k-1]文件内容复制*/
content[fnode1[k-1]->block_num] = content[fnode5->block_num];
while (fnode5 != fnode3)
{
if (fnode5->brother != NULL)
{
if (fnode5->isCld == 1)//待复制节点为第一个兄弟指针记录首节点
{
fnode5 = fnode5->brother;
//fnode6 = fnode7->child;
}
else//待复制节点不是第一个兄弟指针记录左兄弟节点
{
fnode5 = fnode5->brother;
if (fnode5->isCld != 1)
{
if (fnode6->brother != NULL)
{
fnode6 = fnode6->brother;
}
//fnode6 = fnode6->brother;
}
}
break;
}
else
{
fnode5 = fnode5->parent;
fnode7 = fnode7->parent;
fnode6 = fnode6->parent;
}
}
if (fnode5 == fnode3)//全部复制完毕
{
break;
}
}
else if (fnode5->child == NULL)//为目录文件且无子节点
{
while (fnode5 != fnode3)
{
if (fnode5->brother != NULL)
{
//fnode5 = fnode5->brother;
if (fnode5->isCld == 1)//待复制节点为第一个兄弟指针记录首节点
{
fnode5 = fnode5->brother;
//fnode6 = fnode6->child;
}
else//待复制节点不是第一个兄弟指针记录左兄弟节点
{
fnode5 = fnode5->brother;
if (fnode5->isCld != 1)
{
if (fnode6->brother != NULL)
{
fnode6 = fnode6->brother;
}
}
}
break;
}
else
{
fnode5 = fnode5->parent;
fnode7 = fnode7->parent;
fnode6 = fnode6->parent;
}
}
if (fnode5 == fnode3)
{
break;
}
}
else //为目录文件且有子节点
{
if (fnode5->isCld != 1)//当前目录节点不是第一子节点时
{
fnode7 = fnode6->brother;
}
else
{
fnode7 = fnode7->child;
}
fnode5 = fnode5->child;
//fnode7 = fnode7->child;
//fnode6 = fnode6->child;
}
}
}
/*复制终止条件fnode->parent=parent1*/
cout << fnode3->filename;
cout << "目录复制成功" << endl;
}
}
}
}
}
}
}
else if (str.substr(0, 3).compare("ren") == 0)
{
//ren命令可以重命名一个存在于当前目录下的文件或目录
if (str.substr(3).compare("?") == 0)
{
cout << "ren命令可以重命名一个存在于当前目录下的文件或目录,具体格式:ren+name+newname" << endl;
}
else
{
if (str.substr(3) == "")
{
cout << "name命名错误" << endl;
}
else if (str.find_last_of(' ')==3)
{
cout << "newname命名错误" << endl;
}
else
{
//str.find(' ', 4);找到第二个空格位置
//str.substr(str.find(' ', 4)+1)新名字
//str.substr(4,(str.find(' ', 4) - 4))就名字
if (repeatname(str.substr(4, str.find(' ', 4)-4)) == 0)
{
cout << "当前目录下不存在该文件或目录,无法重命名" << endl;
}
else
{
fnode *fnode2 = parent1->child;
while (fnode2->filename != str.substr(4, str.find(' ', 4) - 4))
{
fnode2 = fnode2->brother;
}
fnode2->filename = str.substr(str.find(' ', 4) + 1);
cout << fnode2->filename <<"命名成功"<< endl;
}
}
}
}
else if (str.substr(0, 3).compare("wrt") == 0)//wrt+name+content 写入文件内容
{
if (str.substr(3).compare("?") == 0)
{
cout << "wrt命令可以在当前目录下写内容至一个已存在的文件,具体格式:wrt+name+content" << endl;
}
else
{
if (str.substr(3) == "")
{
cout << "name命名错误" << endl;
}
else if (str.find_last_of(' ') == 3)
{
cout << "content内容错误" << endl;
}
else
{
if (repeatname(str.substr(4, str.find(' ', 4) - 4)) != 1)
{
cout << "当前目录下不存在该文件,无法写入内容" << endl;
}
else
{
fnode *fnode2 = parent1->child;
while (fnode2->filename != str.substr(4, str.find(' ', 4) - 4))
{
fnode2 = fnode2->brother;
}
content[fnode2->block_num] = str.substr(str.find(' ', 4) + 1);
if (content[fnode2->block_num] == str.substr(str.find(' ', 4) + 1))
{
cout << content[fnode2->block_num] << "写入文件" << fnode2->filename << "成功" << endl;
}
else
{
cout << "写入失败" << endl;
}
}
}
}
}
else if (str.substr(0, 3).compare("typ") == 0)//typ命令可以查看当前目录下一个文件的内容
{
if (str.substr(3).compare("?") == 0)
{
cout << "typ命令可以读取当前目录下一个文件的内容,具体格式:typ+name" << endl;
}
else
{
if (str.substr(3) == "")
{
cout << "name命名错误" << endl;
}
else
{
if (repeatname(str.substr(4)) != 1)
{
cout << "当前目录下不存在该文件,无法读取内容" << endl;
}
else
{
fnode *fnode2 = parent1->child;
while (fnode2->filename != str.substr(4))
{
fnode2 = fnode2->brother;
}
if (content[fnode2->block_num] != "")
{
if (cout << "文件" << fnode2->filename << " 所占物理盘块为:" <<fnode2->block_num << " 内容为:" << content[fnode2->block_num] << " ")
{
cout << "读取成功" << endl;
}
else
{
cout << "读取失败" << endl;
}
}
else
{
cout << "文件" << fnode2->filename << " 所占物理盘块为:" << fnode2->block_num << " 内容为:空" << endl;
}
}
}
}
}
else if (str.substr(0, 3).compare("cls") == 0)
{
//dir命令可以显示当前目录下的所有文件和目录,具体格式:dir
if (str.substr(3).compare("?") == 0)
{
cout << "cls命令为清屏命令,具体格式:cls" << endl;
}
else
{
system("cls");
}
}
else if (str.substr(0, 3).compare("mem") == 0)
{
//dir命令可以显示当前目录下的所有文件和目录,具体格式:dir
if (str.substr(3).compare("?") == 0)
{
cout << "mem命令可以查看模拟物理盘块的使用情况,具体格式:mem" << endl;
}
else
{
//block[block_size];
cout << "物理盘块的使用情况为:(其中-99表示未占用物理盘块) " ;
for (int i = 0; i < block_size; i++)
{
cout << block[i] << " ";
}
cout << endl;
//cout << "其中-99表示未占用物理盘块" << endl;
}
}
else if (str.substr(0, 4).compare("exit") == 0)
{
//dir命令可以显示当前目录下的所有文件和目录,具体格式:dir
if (str.substr(4).compare("?") == 0)
{
cout << "exit命令为退出命令,具体格式:exit" << endl;
}
}
else if (str.substr(0, 3).compare("dir") == 0)
{
//dir命令可以显示当前目录下的所有文件和目录,具体格式:dir
if (str.substr(3).compare("?") == 0)
{
cout << "dir命令可以显示当前目录下的所有文件和目录,具体格式:dir" << endl;
}
else
{
fnode* fnode2;
//cout << "当前目录下所有文件和目录为:";
if (parent1->child == NULL)
{
cout << "空" << endl;
}
else
{
//cout << parent1->child->filename << " ";
fnode2 = parent1->child;
if (fnode2->type == 1)
{
if (content[fnode2->block_num] == "")
{
cout << "文件 " << parent1->child->filename << " " <<"空"<< endl;
}
else
{
cout << "文件 " << parent1->child->filename << " " << content[fnode2->block_num] << endl;
}
//cout << "文件 " << parent1->child->filename << " "<< content[parent1->child ->block_num]<<endl;
}
else
{
cout << "目录 " << fnode2->filename << endl;
}
while (fnode2->brother != NULL)
{
//cout << fnode2->brother->filename << " ";
fnode2 = fnode2->brother;
if (fnode2->type == 1)
{
if (content[fnode2->block_num] == "")
{
cout << "文件 " << fnode2->filename << " " << "空" << endl;
}
else
{
cout << "文件 " << fnode2->filename << " " << content[fnode2->block_num] << endl;
}
//cout << "文件 " << fnode2->filename << " " << content[fnode2->block_num] << endl;
}
else
{
cout << "目录 " << fnode2->filename << endl;
}
}
//cout << endl;
}
//delete fnode2; //释放new分配的动态指针
}
}
//fnode2 = parent1;
/*while (fnode2->filename != fnode1[0]->filename)//当前父节点不是根时输出从根结点开始的所有目录
{
fnode2 = parent1->parent;
cout << fnode2->filename << ">";
}
*/
cout << current_path->filename << ">";
getline(cin, str);
//cin >> str;
}
else
{
break;
}
}
cout << "退出文件管理系统成功" << endl;
return 0;
}
五、 模块详解
5.1 文件操作
命令 | 作用 |
echo命令 | echo命令可以在当前目录下创建一个文件 |
del命令实现 | del命令可以在当前目录下删除一个已存在的文件 |
wrt命令实现 | wrt命令可以在当前目录下写内容至一个已存在的文件 |
typ命令实现 | typ命令可以读取当前目录下一个文件的内容 |
ren命令实现 | ren命令可以重命名一个存在于当前目录下的文件 |
cp命令实现 | cp命令可以复制当前目录下一个指定的文件至指定的目录 |
5.2 目录操作
命令 | 作用 |
md命令 | md命令可以在当前目录下创建一个目录文件 |
cd命令 | cd命令可以进入下一级目录或返回上级目录、根目录 |
der命令 | der命令可以在当前目录下删除一个已存在的目录 |
cp命令实现 | cp命令可以复制当前目录下一个指定的目录至指定的目录 |
ren命令实现 | ren命令可以重命名一个存在于当前目录下的目录 |
dir命令 | dir命令可以显示当前目录下的所有文件和目录 |
mem命令 | mem命令可以查看模拟物理盘块的使用情况 |
5.3 主要函数
1) void freeblock(fnode *fnode0, fnode *fnode2)
a) 函数作用:主要是用来释放参数节点
b) 首先先通过seeknum函数来找到要释放节点在节点数组中的数组下标值,用来最后将数组下标值对应节点赋NULL。
c) 判断所要释放节点在链表中的位置,是否为上一级目录的子节点,若为子节点需要判断其兄弟节点是否为空,在不为空的情况下将兄弟节点赋为子节点,否则将子节点设置为NULL,若不为子节点要判断其兄弟节点是否为空,不为空的情况下要将左兄弟节点的兄弟节点设置为右兄弟节点。为空的情况下要将左兄弟节点的兄弟节点设置为NULL。
d) 在将节点从链表中隔离开后判断是否为文件节点,若为文件节点要将对应字符串内容设置为“”,最后释放节点指针。
2) int seeknum(fnode *fnode2)
a) 函数作用:主要是用来遍利整个节点数组找到参数节点的数组下标。
b) 设置for循环在指针不为空的情况下判断节点名是否相同、类型是否相同、父节点是否相同,都相同的情况下返回。
3) int repeatname(string s)
a) 函数作用:主要是用来遍利整个节点数组判断当前目录下节点是否重名。
b) 设置for循环:在指针不为空的情况下判断节点名是否相同、类型是否相同、父节点是否相同,都相同的情况下且类型为1返回1表示与某一文件名重复,类型为2返回2表示与某一目录名重复。
4) void show_current_path()
函数作用:主要是用来显示当前路径显示类似WP:\a\b这样的目录名称。
5) void initialize()
函数作用:主要是用来初始化物理块。
6) int seekfreeblock()
函数作用:主要是用来遍历整个物理块数组判断是否有可用空间。
7) void help()
函数作用:主要是用来帮助。
8) int display()
a) 函数作用:主要是用来显示文件系统开始的介绍界面,且可以判断输入用户名和密码是否正确。
b) 设置while(1)循环判断输入内容是否正确。
六、 实验演示
1) 主界面如图13
图13主界面
2) help命令展示如图14:
图13 help命令
3) echo命令展示如图15:
图15 echo命令
4) del命令展示如图16:(mem命令可以观察是否删除正确)
图16 del命令
5) wrt命令展示如图17:
图17 wrt命令
6) typ命令展示如图18:
图18 typ命令
7) ren命令展示如图19:
图19 ren命令
8) md命令展示如图20:
图20 md命令
9) cd命令展示如图21:
图21 cd命令
10) cp命令展示如图22:(mem命令可以观察是否删除正确)
图22(1)说明:首先在a目录下建立子目录c、子目录d、文件e,然后进入子目录c建立文件a、目录b、文件c,再返回上一级目录然后进入子目录d建立不同内容目录a、文件b、目录c说明cp命令的可行性,再返回上一级目录写入文件e内容,最后通过dir命令展示a目录下的内容。
图22 (1)cp命令
图22(2)说明:调用cp命令将目录a拷贝至WP:\b:
图22 (2)cp命令
图22(3)说明:进入b目录观察cp命令是否正确:
图22 (3)cp命令
11) mem命令展示如图23:
图23 mem命令
12) der命令展示如图24:
图24(1)说明:首先返回根目录显示当前目录下内容:
图24(1) der命令
图24(2)说明:der目录a观察,mem显示了a删除的正确性。
图24(2) der命令
七、 程序总结
编写调试过程中遇到许多问题,一类是指针指向空还继续操作造成程序停止运行,经检查后修改正确,一类是逻辑上出现错误导致级联删除或者目录复制出错,经改正后成功实现。
主要的难点在于兄弟结点和子结点的处理,所以在程序中引入了isBro这个标志位来判断是否是兄弟结点即与父节点是否在同级目录,若不在同级目录则父节点为真父节点(是指向本目录而非本目录中上个文件)。还有一个难点是在进行递归调用删除或复制目录时发现处理目录和目录下的内容不太合适放在同一个递归程序中,所以单独写了两个函数处理目录的复制或删除,然后对应两个递归程序处理目录下的所有内容。
待改进的问题。
a) 这个模拟文件系统过程只能在内存中实现,没有真正达到物理结构上的存储。
b) 由于没有很好的了解真实文件系统中显示格式,而是简单设计的一个输入输出格式
c) 另因为对输入命名上的处理上有点瑕疵,所有在同一父节点的节点名称不能重复,这个限制是由于repeatname()这个函数设定出现的,当时为了能够避免出现重名导致无法查找到正确节点的bug,我在echo和md操作生成节点时限制了重名这一问题,仍有改进空间。