虚拟文件目录系统
虚拟文件目录系统
- 问题描述
- 基本要求
- 代码实现
- 结果显示
问题描述
本设计需完成两部分工作:一个是定义并实现一称为CatalogTree的ADT,用它来表达字符集合组成的有序树;另一个是shell的应用程序,用它来模拟文件目录系统,并提供模拟操作界面。
CatalogTree 的组织结构如下图(带父节点指针的儿子-兄弟链树):
针对于目录系统,CatalogTree的结点存放的数据内容为字符串,每个结点对应一个目录项,该目录项可以是目录,也可以是文件,如果是目录就可以再存放其他目录或文件,即非叶节点;如果是文件就是叶节点。从根节点到该节点路径所有结点的字符串用‘/‘进行组合就是该目录项的绝对路径,用来唯一的标识该目录,例如:/usr/li/email/student/。
目录系统具有如下的基本操作:
1) dir 列出当前目录下所有目录项
2) cd 打出当前目录的绝对路径
3) cd .. 当前目录变为当前目录的父目录
4) cd str 当前目录变为str所表示路径的目录
5) mkdir str 在(当前目录下)创建一个子目录(名为str)
6) mkfile str 在(当前目录下)创建一个文件(名为str)
7) delete str 删除(当前目录下)名为str的目录或文件
基本要求
1.描述并实现CatalogTree的ADT,包括其上的基本操作:如插入一个结点,寻找一个节点,返回一个结点的最左儿子等(具体情况依据应用自定);
2.应用CatalogTree的ADT实现一个完成文件目录系统的shell应用程序;
3.该Shell是一个不断等待用户输入命令的解释程序,根据用户输入的命令来完成相关操作,直到退出(quit),命令名及其含义如上所述。
4.目录树结构可以保存(save)到文件中,也可以从文件中读出(load *.dat);
5.Dir命令的结果应能够区分是子目录还是文件;
6.应对命令4)-7)中的str区分是绝对路径还是相对路径。
核心代码
- CatalogTree.h
#include<iostream>
#include<string.h>
#include <stdio.h>
using namespace std;
struct TreeNode {//树节点
struct TreeNode *parent;//父指针
struct TreeNode *FirstChild;//第一个儿子指针
struct TreeNode *xiongdi;//兄弟指针
bool flag_file;//true表示文件,false表示目录
char fileName[100];//文件名
int depth;//深度
int size;//子文件数目
};
typedef struct TreeNode *Position;//为了使用方便
typedef struct TreeNode *Tree;
typedef struct TreeNode *ptr;
class CatalogTree;
void cd_Position(CatalogTree *a, Position x);//根据位置寻找路径
void deletePtr(CatalogTree *a, ptr t);//根据位置删除
class CatalogTree {
public:
TreeNode *root;
ptr currentPosition;
public:
CatalogTree();//构造函数
~CatalogTree() {//析构函数
deletePtr(this, root);
};
void mkdir(char *name, Position t);//创建目录
void mkfile(char *name, Position t);//创建文件
void ListDir();//列出当前目录下的文件
void Delete(char *str);//删除文件或目录
void cd();//打印当前路径
void cdStr(char *str);//跳到指定路径
void cdPre();//跳到父路径
void save(char *filename);//将目录结构保存至文件
void load(char *filename);//将目录结构从文件载入
void ListDirToFile(Position D, int Depth, FILE *file);//从文件打印出目录结构
void size(char *dirName);//打印当前目录下的文件个数
};
CatalogTree::CatalogTree()
{ //构造方法
ptr m_root = (struct TreeNode*)malloc(sizeof(struct TreeNode));
m_root->FirstChild = NULL;
memset(m_root->fileName, 0, sizeof(m_root->fileName));
m_root->fileName[0] = '/';
m_root->flag_file = false;
m_root->parent = NULL;
m_root->xiongdi = NULL;
m_root->size = 0;
root = m_root;
currentPosition = root;
};
void CatalogTree::size(char *dirName) {//打印出当前路径下某目录的文件数
Position t;
bool flag = false;
for (t = currentPosition->FirstChild; t != NULL; t = t->xiongdi) {
if (strcmp(t->fileName, dirName) == 0) {
flag = true;
break;
}
}
if (strcmp(dirName, "/") == 0) flag = true;
if (flag == false) {
printf(" 没有该目录或文件\n");
return;
}
if (strcmp(dirName, "/") == 0)//打印根目录的文件数
printf("size of %s : %d\n", dirName, root->size);
else
printf("size of %s : %d\n", dirName, t->size);
}
void CatalogTree::ListDirToFile(Position D, int Depth , FILE *file)//从文件打印出目录结构
{
ptr temp;
if (D!=NULL) {
for (int i = 0; i < Depth; i++) {
fprintf(file , "\t");
}
if (D->flag_file == true) {
//printf("%s .f\n", D->fileName);
fprintf(file, "%s .f\n", D->fileName);
}
else {
//printf("%s .d\n", D->fileName);
fprintf(file, "%s .d\n", D->fileName);
}
if (D->flag_file == false)
for (temp = D->FirstChild; temp != NULL; temp = temp->xiongdi)
ListDirToFile(temp, Depth + 1 , file);
}
}
void CatalogTree::save(char *filename) {//将目录结构保存至文件
FILE* file = fopen(filename, "w");
if (file == NULL) {
printf(" 文件打开失败\n");
return;
}
ListDirToFile(this->root, 0 , file);//将目录结构存入文件
fclose(file);
//printf(" 保存文件成功\n");
}
void ListFileToTree(CatalogTree *T , Position D ,char *preDir, int preDepth, FILE *file)//将文件中内容载入
{
char buf[120];
char type[3];
char subBuf[100];
memset(buf, 0, sizeof(buf));
memset(type, 0, sizeof(type));
memset(subBuf, 0, sizeof(subBuf));
int i;
int t_n = 0;
fgets(buf, sizeof(buf), file);//一行一行获取数据
if (strlen(buf) == 0) return;//如果读到文件末尾,结束
strncpy(type, buf + strlen(buf) - 3, 2);//获取改行数据是文件还是目录
for (i = 0; buf[i] == '\t'; i++) {//计算改行中有多少缩进,可以判断出哪一级的文件
t_n++;
}
if (i == 0) ListFileToTree(T , D , "/", 0 , file);//如果没有缩进,则证明是第一行根路径
else
{
strncpy(subBuf, buf + t_n, strlen(buf) - 4 - t_n);//获取改行中文件或目录的文件
if (t_n > preDepth) {//如果改行中缩进比上一行多,证明改行文件/目录为上一行目录的子文件
T->cdStr(preDir);//改变当前指标为上一行目录
}
else if (t_n < preDepth) {//如果缩进小于上一行,每小一行,则当前指针做一次“回到上一路径”操作
for (int j = t_n; j < preDepth; j++) {
T->cdPre();
}
}
//默认深度与上一行文件/目录属于同一深度
if (strcmp(type, ".d") == 0) {//如果是目录
T->mkdir(subBuf, T->currentPosition);
//T->cdStr(subBuf);
}
else {//如果是文件
T->mkfile(subBuf, T->currentPosition);
}
ListFileToTree(T, T->currentPosition,subBuf, t_n, file);//进入下一行
}
}
void CatalogTree::load(char *filename) {//搜索当前位置的路径,使用递归
FILE *file = fopen(filename, "r");
if (file == NULL) {
printf(" 文件打开失败,请检查文件名是否正确\n");
return;
}
ListFileToTree(this, currentPosition,"/" , 0, file);
fclose(file);
//printf(" 载入成功\n");
cdStr("/");
}
void cd_Position(CatalogTree *a , Position x) {//搜索当前位置的路径,使用递归
if (x == a->root) {
printf("/");
return;
}
else
{
cd_Position(a, x->parent);
printf("%s/", x->fileName);
}
}
void CatalogTree::cd()//输出当前路径
{
ptr x = currentPosition;
cd_Position(this,x);
//printf("\n");
}
void CatalogTree::cdStr(char *str)//根据路径改变currentPosition
{
ptr temp;
if (str[0] == '/') {//如果第一个字符为'/',证明是绝对路径
ptr t = root;
const char *d = "/";
char *p;
p = strtok(str, d);//分隔字符串
bool flag;
while (p)
{
flag = false;
for (temp = t->FirstChild; temp != NULL; temp = temp->xiongdi) {
if (strcmp(temp->fileName, p) == 0&&temp->flag_file == false) {
t = temp;//不断修改t的值
flag = true;
break;
}
}
if (flag == false ) {//如果为false,则用户输入的路径应该有错
printf(" 没有该命令\n");
return;
}
//printf("%s\n", p);
p = strtok(NULL, d);
}
currentPosition = t;
}
else {//相对路径
const char *d = "/";
char *p;
ptr t = currentPosition;
p = strtok(str, d);
bool flag;
while (p)
{
flag = false;
for (temp = t->FirstChild; temp != NULL; temp = temp->xiongdi) {
if (strcmp(temp->fileName, p) == 0&&temp->flag_file == false) {
t = temp;//不断更新当前路径
flag = true;
break;
}
}
if (flag == false) {
printf(" 没有该命令\n");
return;
}
p = strtok(NULL, d);
}
currentPosition = t;
}
}
void CatalogTree::cdPre() {//cd..
if (currentPosition == root) {
printf(" 已经到根路径\n");
return;
}
currentPosition = currentPosition->parent;
//this->cd();
}
void CatalogTree::ListDir()//列出当前目录下所有文件
{
Position t = currentPosition;
ptr temp;
for (temp = t->FirstChild; temp != NULL; temp = temp->xiongdi) {
if (temp->flag_file) {
printf(" %s .f\n", temp->fileName);
}
else {
printf(" %s .d\n", temp->fileName);
}
}
}
void deletePtr(CatalogTree *a , ptr t) {//删除某个文件或目录
ptr temp;
if (t->flag_file) {//如果是文件
temp = t->parent->FirstChild;
if (temp == t) {//如果删除的文件是父亲的第一个儿子,将父亲的儿子指针指向该文件的兄弟,然后释放
t->parent->FirstChild = temp->xiongdi;
free(t);
return;
}
for (temp = t->parent->FirstChild; temp != NULL; temp = temp->xiongdi) {//如果不是父亲的第一个儿子,则找到该节点的位置,让该节点的前驱的兄弟指针指向删除结点的兄弟,然后释放
if (t == temp->xiongdi) {
temp->xiongdi = t->xiongdi;
free(t);
return;
}
}
}
else {//如果是目录
if (t->FirstChild == NULL) {//如果该目录没有文件,则直接删除
if (t == a->root) return;
if (t->parent->FirstChild == t) {
t->parent->FirstChild = t->xiongdi;//如果该目录位于父亲节点的第一个儿子,则将父亲的儿子指针为空,然后删除
}
else
for (temp = t->parent->FirstChild; temp != NULL; temp = temp->xiongdi) {//如果不是第一个儿子,则找到该节点的前驱,将前驱的兄弟指针指向该节点的兄弟,然后释放该节点
if (temp->xiongdi == t) {
temp->xiongdi = t->xiongdi;
break;
}
}
free(t);
}
else {//如果该目录下有文件,则进行递归删除
while (t->FirstChild != NULL) {
for (temp = t->FirstChild; temp != NULL; temp = temp->xiongdi) {//删除时应
if (temp->xiongdi == NULL) {
deletePtr(a , temp);
break;
}
}
}
deletePtr(a , t);///////////不用在释放
}
}
}
void CatalogTree::Delete(char *str) {//删除
//删除操作
Position t;
bool flag = false;
for (t = currentPosition->FirstChild; t != NULL; t = t->xiongdi) {
if (strcmp(t->fileName, str) == 0) {
flag = true;
break;
}
}
if (flag == false) {
printf(" 没有该目录或文件\n");
return;
}
deletePtr(this, t);
}
void CatalogTree::mkdir(char *name, Position t) {//创建文件夹
ptr temp;
for (temp = t->FirstChild; temp != NULL; temp = temp->xiongdi) {
if (strcmp(temp->fileName, name) == 0) {//&&temp->flag_file == false
cout << " 不能产生相同名字的目录或文件,创建失败" << endl;
return;
}
}
temp = (ptr)malloc(sizeof(struct TreeNode));//创建树节点
temp->parent = t;
temp->FirstChild = NULL;
temp->flag_file = false;//false表示该节点为目录
temp->size = 0;
strcpy(temp->fileName, name);
temp->xiongdi = t->FirstChild;
t->FirstChild = temp;
}
void CatalogTree::mkfile(char *name, Position t) {//创建文件
ptr temp;
for (temp = t->FirstChild; temp != NULL; temp = temp->xiongdi) {
if (strcmp(temp->fileName, name) == 0 ) {//&& temp->flag_file == true
cout << " 不能产生相同名字的文件,创建失败" << endl;
return;
}
}
temp = (ptr)malloc(sizeof(struct TreeNode));//创建树结点
temp->parent = t;
temp->FirstChild = NULL;
temp->flag_file = true;
temp->size = 1;
strcpy(temp->fileName, name);
temp->xiongdi = t->FirstChild;
t->FirstChild = temp;
for (temp = t; t != NULL; t = t->parent) {
t->size++;
}
}
- CatalogTree.cpp
#include"CatalogTree.h"
#include<stdio.h>
int main() {
CatalogTree T;
printf("***********************************************************\n");
printf("菜单 虚拟文件目录系统\n");
printf("dir 列出当前目录下所有目录项\n");
printf("cd 查看当前路径\n");
printf("cd dir 当前目录变为str所表示路径的目录\n");
printf("cd .. 当前目录变为当前目录的父目录\n");
printf("mkdir str 在当前目录下创建一个名为str的子目录\n");
printf("mkfile str 在当前目录下创建一个名为str的文件\n");
printf("delete str 删除当前目录下名为str的目录或文件\n");
printf("save *.dat 保存虚拟目录到*.dat文件中\n");
printf("load *.dat 载入*.dat文件中的虚拟目录\n");
printf("size str 查看当前某子目录下的文件数\n");
printf("quit 退出\n");
printf("***********************************************************\n");
char s[1000];
char subs[1000];
while (true) {
T.cd();
printf("->");
memset(s, 0, sizeof(s));
memset(subs, 0, sizeof(subs));
gets_s(s);
if (strlen(s) > 3) {
if (s[0] == 'c'&&s[1] == 'd') {//cd ..
strncpy(subs, s + 3, strlen(s)-3);
if (strcmp(subs, "..") == 0) {
T.cdPre();
}
else {//cd str
T.cdStr(subs);
}
}
else if (strlen(s)>6 && s[0] == 'm'&&s[1] == 'k'&&s[2] == 'd'&&s[3] == 'i'&&s[4] == 'r') {//mkdir str
strncpy(subs, s + 6, strlen(s) - 6);
T.mkdir(subs,T.currentPosition);
}
else if (strlen(s)> 7 && s[0] == 'm'&&s[1] == 'k'&&s[2] == 'f'&&s[3] == 'i'&&s[4] == 'l'&&s[5] == 'e') {//mkfile str
strncpy(subs, s + 7, strlen(s) - 7);
T.mkfile(subs,T.currentPosition);
}
else if (strlen(s) > 7 && s[0] == 'd'&&s[1] == 'e'&&s[2] == 'l'&&s[3] == 'e'&&s[4] == 't'&&s[5] == 'e') {//delete str
strncpy(subs, s + 7, strlen(s) - 7);
T.Delete(subs);
}
else if (strlen(s) == 4 && strcmp(s,"quit")==0) {//quit
//T.save("F:\\mulu.dat");
return 0;
}
else if (strlen(s) == 4 && strcmp(s, "help") == 0) {//help
printf("***********************************************************\n");
printf("dir 列出当前目录下所有目录项\n");
printf("cd 查看当前路径\n");
printf("cd dir 当前目录变为str所表示路径的目录\n");
printf("cd .. 当前目录变为当前目录的父目录\n");
printf("mkdir str 在当前目录下创建一个名为str的子目录\n");
printf("mkfile str 在当前目录下创建一个名为str的文件\n");
printf("delete str 删除当前目录下名为str的目录或文件\n");
printf("save *.dat 保存虚拟目录到*.dat文件中\n");
printf("load *.dat 载入*.dat文件中的虚拟目录\n");
printf("size str 查看当前某子目录下的文件数\n");
printf("quit 退出\n");
printf("***********************************************************\n");
}
else if (strlen(s) > 5 && s[0] == 's'&&s[1] == 'a'&&s[2] == 'v'&&s[3] == 'e') {//save
strncpy(subs, s + 5, strlen(s) - 5);
char filePath[100] = "F:\\";
strcat(filePath, subs);
T.save(filePath);
T.ListDirToFile(T.root, 0, stdout);
printf("保存成功\n");
//T.mkfile(subs, T.currentPosition);
}
else if (strlen(s) > 5 && s[0] == 'l'&&s[1] == 'o'&&s[2] == 'a'&&s[3] == 'd') {//load *.dat
strncpy(subs, s + 5, strlen(s) - 5);
char filePath[100] = "F:\\";
strcat(filePath, subs);
T.load(filePath);
T.ListDirToFile(T.root, 0, stdout);
printf("载入成功\n");
}
else if (strlen(s) > 5 && s[0] == 's'&&s[1] == 'i'&&s[2] == 'z'&&s[3] == 'e') {//size str
strncpy(subs, s + 5, strlen(s) - 5);
T.size(subs);
}
else {
printf(" 没有此命令\n");
}
}
else {
if (strlen(s) == 3) {
if (strcmp(s, "dir") == 0) {//dir
T.ListDir();
}
else {
printf(" 没有此命令\n");
}
}
else if (strlen(s) == 2) {//cd
if (strcmp(s, "cd") == 0) {
T.cd();
printf("\n");
}
else
printf(" 没有此命令\n");
}
else if (strlen(s) == 1) {//p
if (s[0] == 'p') {//打印目录结构
T.ListDirToFile(T.root, 0, stdout);
}
else {
printf(" 没有此命令\n");
}
}
else {
printf(" 没有此命令\n");
}
}
}
return 0;
}
结果显示
考虑到输出结果太多,仅显示部分结果。
上一篇: ansible 小试牛刀