欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

虚拟文件目录系统

程序员文章站 2022-05-10 13:07:06
...

虚拟文件目录系统

  • 问题描述
  • 基本要求
  • 代码实现
  • 结果显示

问题描述

本设计需完成两部分工作:一个是定义并实现一称为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;
}

结果显示

考虑到输出结果太多,仅显示部分结果。

虚拟文件目录系统

虚拟文件目录系统

附:源代码文件 实验报告文件