本文是在博主木子Link的代码基础上加以修改,并添加了一些文件保护功能和写入文件大小的功能,并在自己的理解上加上注释。操作系统 文件与磁盘管理 C语言(模拟文件目录)_模拟文件系统中的树型目录结构,并模拟实现常用dos命令。如:dir, md, cd, rd, cre-CSDN博客
一、设计要求
通过模拟磁盘,完成操作系统的文件管理功能,掌握包括目录结构的管理、外存空间的分配与释放以及空闲空间管理三部分。为写入模拟磁盘的数据文件建立目录,目录可以是单级文件目录、双级文件目录、树形结构目录。在目录中选择某个文件可以将其数据读入模拟内存。
1、通过初始化操作建立一个模拟磁盘,在模拟磁盘中保存目录和文件内容。创建该模拟磁盘时可创建初始的根目录内容、文件分配表。
2、文件目录项(可以采用FCB格式)应包括文件名、类型(目录 or文件)、创建日期、大小、第一个磁盘块块号。
3、目录管理需支持:
新建目录:在目录中新建空目录;
删除目录:删除空目录
为文件建立目录项:一个文件创建成功后,为该文件创建目录项,并将文件和相关信息写入目录;
删除文件:删除目录中某个文件,删除其在磁盘中的数据,并删除目录项。如果被删除文件已经读入内存应阻止删除,完成基本的文件保护。
二、设计思想
本设计采用二叉树模拟树形文件管理结构
1、文件结构如下:
所有该节点下的左子树为同一级文件夹下的目录,该节点下所有的右子树为该节点的子文件。
2、算法流程图
三、主要数据结构及其说明
1、文件结构体FCB块如下:
typedef struct FCB
{ // 文件或目录控制块
char name[10]; // 文件或目录名
int size; // 文件或目录大小,目录大小可设置为 0
char type; // 类型,1 为文件,2 为目录
int first; // 外存起始位置 -1被占 -2未被占
char datetime[128]; // 日期时间,格式为 yyyymmdd hhmmss
int flagg; // 文件状态,1为打开,0为关闭
char neirong[20]; // 文件内容
struct FCB *next; // 下一个兄弟节点
struct FCB *child; // 第一个孩子节点
struct FCB *parent;
} F;
2、模拟内存分配情况
采用位示图模拟:随机分配1与0模拟表示内存已被占用情况:用8*8的二维数组记录,FAT块(记录文件的地址情况):用64位的一维数组记录。
例如:位示图如下
i表示行,j表示列
FAT[i * 8 + j]=-1或-2
这里的FAT块是一个一维数组,用于存储文件的位置
当位示图为0时,FAT的下标记录位示图地址,它的值表示状态。
f.size为文件大小;此时,FAT记录文件所占位示图位置。最后用一个哨兵记录该文件的起始位置并返回;
其余模拟文件管理的切换、创建、删除、查看均使用了二叉树的遍历算法。
四、核心代码
1、用FAT记录文件的位置
int findNULL(int size)
{
int y = 0;
int a, k = 0;
int m[64] = {0};
int h = 0;
for (k = 0; k < size; k++)
{
a = 0;
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
if (b[i][j] == 0)
{
h = i * 8 + j; // 转化为盘块号
m[k] = h;
b[i][j] = 1;
FAT[h] = h;
a = 1;
break;
}
}
if (a == 1)
{
break;
}
}
}
return m[0];//返回该文件的起始位置
}
2、输出文件的地址
void fatShow() // 展示文件分配表
{
int j = 0;
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
printf("%d ", FAT[i * 8 + j]);
}
printf("\n");
}
printf("***\n");
F *np = p; // 指向当前节点
while (np->parent != NULL)
{
np = np->parent;
}
inOrder(np);//将当前节点传给inOrder()函数,用于遍历二叉树下所有文件
}
// 遍历
void inOrder(F *root)
{
if (root != NULL)
{
inOrder(root->child);
if (root->type == 1)//如果节点类型为文件类型,就输出该节点
{
printf("%s ", root->name);
printf("文件的地址 ");
int k = 0;
int i = root->first;
for (i; i < 64; i++)
{
if (FAT[i] != -1 && FAT[i] != -2 && k < root->size)
{
printf("%d ", FAT[i]);
k++;//用于记录文件长度
}
if (k == root->size)//当输出位置达到文件长度跳出该循环即可输出下一个文件地址
{
printf("\n");
break;
}
}
}
inOrder(root->next);
}
}
3、文件保护
root->flagg用于记录文件状态。这里同样用到的是二叉树的递归遍历
void odr(F *root, char *name)
{
if (root != NULL)
{
odr(root->child, name);//左
{
if (strcmp(root->name, name) == 0)//中
{
printf("close为关闭文件,r为读文件\n");
char w[8];
scanf("%s", w);
if (strcmp(w, "r") == 0)
{
root->flagg = 1;
printf("%s\n", root->neirong);
}
if (strcmp(w, "close") == 0)
{
root->flagg = 0;
printf("关闭成功\n");
}
}
}
odr(root->next, name);//右
}
}
void ZT(char *name)
{
F *np = p;
odr(np, name);
}
其它文件切换,删除目录文件都用上了二叉树的遍历,这里就不赘述了。
4、所有代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
typedef struct FCB
{ // 文件或目录控制块
char name[10]; // 文件或目录名
int size; // 文件或目录大小,目录大小可设置为 0
char type; // 类型,1 为文件,2 为目录
int first; // 外存起始位置 -1被占 -2未被占
char datetime[128]; // 日期时间,格式为 yyyymmdd hhmmss
int flagg; // 文件状态,1为打开,0为关闭
char neirong[20]; // 文件内容
struct FCB *next; // 下一个兄弟节点
struct FCB *child; // 第一个孩子节点
struct FCB *parent;
} F;
F *p = NULL; // 指向当前目录
int b[100][100]; // 位视图
int FAT[100]; // 文件分配表
void updateTable(int first, int size); // 更新fat表
int findNULL(int size);
void getTime(F *f) // 获取当前时间并保存
{
time_t t;
char buf[128];
memset(buf, 0, sizeof(buf));
struct tm *tmp;
t = time(NULL);
tmp = localtime(&t);
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tmp);
for (int i = 0; i < 128; i++)
{
f->datetime[i] = buf[i];
}
}
void byteCreat() // 随机分配空间
{
srand((unsigned)time(NULL));
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
b[i][j] = rand() % 2;
printf("%d ", b[i][j]);
if (b[i][j] == 0)
{
FAT[i * 8 + j] = -2; // 未被占;
}
else if (b[i][j] == 1)
{
FAT[i * 8 + j] = -1; //-1:被占
}
}
printf("\n");
}
}
void byteShow() // 展示位示图
{
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
printf("%d ", b[i][j]);
}
printf("\n");
}
}
// 遍历
void inOrder(F *root)
{
if (root != NULL)
{
inOrder(root->child);
if (root->type == 1)
{
printf("%s ", root->name);
printf("文件的地址 ");
int k = 0;
int i = root->first;
for (i; i < 64; i++)
{
if (FAT[i] != -1 && FAT[i] != -2 && k < root->size)
{
printf("%d ", FAT[i]);
k++;
}
if (k == root->size)
{
printf("\n");
break;
}
}
}
inOrder(root->next);
}
}
void fatShow() // 展示文件分配表
{
int j = 0;
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
printf("%d ", FAT[i * 8 + j]);
}
printf("\n");
}
printf("***\n");
F *np = p; // 指向当前节点
while (np->parent != NULL)
{
np = np->parent;
}
inOrder(np);
}
void makedir(int a) // 创建文件(夹)
{
char name[10];
F *np = p; // 指向当前目录
scanf(" %s", name);
F *f = (F *)malloc(sizeof(F)); // 新建空白目录
strcpy(f->name, name); // 保存名字
if (a == 1) // 建立文件,1为文件 2 为目录
{
f->type = 1;
printf("请输入文件大小");
scanf("%d", &f->size);
printf("输入文件内容:");
char neirong[50];
scanf(" %s", neirong);
strcpy(f->neirong, neirong);
f->first = findNULL(f->size); // 文件的起始地址
f->flagg = 0;
}
if (a == 2) // 目录
{
f->type = 2;
f->first = -2;
f->size = 0;
f->flagg = 0;
}
getTime(f);
f->child = NULL;
f->next = NULL;
f->parent = np; // 父节点
if (np->child == NULL)
{
np->child = f;
}
else // 进入当前子目录,左孩子为同一级文件或目录,它的其它兄弟节点为它的子目录或文件
{
np = np->child; // 指向第一个目录或文件
if (a == 2)
{
if (np->type == 2)
{
while (np->next != NULL)
{
if (np->type == 2 && np->next->type == 1) // 目录新建在目录和文件中间
{
break;
}
np = np->next;
}
f->next = np->next;
np->next = f;
}
}
else if (a == 1)
{
while (np->next != NULL) // 为空跳出循环
{
np = np->next;
}
f->next = np->next;
np->next = f;
}
}
}
int findNULL(int size)
{
int y = 0;
int a, k = 0;
int m[64] = {0};
int h = 0;
for (k = 0; k < size; k++)
{
a = 0;
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
if (b[i][j] == 0)
{
h = i * 8 + j; // 转化为盘块号
m[k] = h;
b[i][j] = 1;
FAT[h] = h;
a = 1;
break;
}
}
if (a == 1)
{
break;
}
}
}
return m[0];
}
void cdir(char *name) // 切换目录
{
F *np = p;
np = np->child;
if (strcmp(np->name, name) == 0) // 名字相同
{
p = np; // 找到并进入
}
else
{
while (strcmp(np->name, name) != 0)
{
np = np->next;
if (strcmp(np->name, name) == 0)
{
p = np; // 找到并进入
break;
}
if (np == NULL)
{
printf("未找到此目录");
break;
}
}
}
}
void printNow() // 展示当前目录下的文件及文件夹
{
F *np = p;
if (p->child == NULL)
{
printf("当前文件夹为空\n");
}
else
{
np = np->child;
printf("%s <DIR> .\n", np->parent->datetime); // 当前目录建立的时间,或者进入终端时间
// printf("%s <DIR> ..\n", np->parent->datetime);
while (np != NULL)
{
printf("%s 类型:%d 名字:%s 大小:%d\n", np->datetime, np->type, np->name, np->size);
np = np->next;
}
}
}
void delDir(char *name) // 删除目录
{
int flag = 0;
F *np = p;
if (np->child == NULL)
printf("无此目录\n");
else
{
np = np->child;
if (strcmp(np->name, name) == 0 && np->child == NULL) // 为第一个
{
p->child = np->next;
free(np);
printf("成功删除此目录\n");
flag = 1;
}
else
{
while (np->next != NULL) // 文件夹下还有目录
{
if (strcmp(np->next->name, name) == 0 && np->next->child == NULL)
{
np->next = np->next->next;
flag = 1;
printf("成功删除此目录\n");
break;
}
np = np->next;
}
}
if (flag == 0)
{
printf("删除失败!无当前目录或当前目录不为空\n");
}
}
}
void delFile(char *name) // 删除文件
{
int flag = 0;
F *np = p;
if (np->child == NULL)
printf("无此文件\n");
else
{
np = np->child;
if (strcmp(np->name, name) == 0 && np->flagg == 0) // 为第一个
{
p->child = np->next;
printf("成功删除此文件\n");
updateTable(np->first, np->size); // 输入文件起始位置,更新fat表
free(np);
flag = 1;
}
else
{
while (np->next != NULL)
{
if (strcmp(np->next->name, name) == 0 && np->flagg == 0)
{
updateTable(np->next->first, np->next->size); // 更新fat表
np->next = np->next->next;
flag = 1;
printf("成功删除此文件\n");
break;
}
np = np->next;
}
}
if (flag == 0 || np->flagg == 1)
{
printf("删除失败!无当前目录或当前目录不为空或该文件正打开\n");
}
}
}
void updateTable(int first, int size) // 更新fat表
{
int k = 0;
for (int i = 0; i < 64; i++)
{
if (FAT[i] != -1 && FAT[i] != -2)
{
FAT[i] = -2;
k++;
b[i / 8][i % 8] = 0;
}
if (k == size)
break;
}
}
void updatelujing() // 更新当前所在位置
{
F *np = p;
F a[100];
int count = 0;
while (np->parent != NULL)
{
a[count] = *np;
np = np->parent;
count++;
}
for (int i = count - 1; i > 0; i--)
{
printf("%s\\", a[i].name);
}
printf("%s\\", p->name);
printf(">");
}
void meum()
{
printf(" ******************************************************\n\n");
printf(" * 欢迎使用文件模拟系统 *\n \n");
printf(" ******************************************************\n\n");
printf(" 该系统目前支持如下功能:\n");
printf(" 1.切换到当前路径下的某文件夹: cd + 文件夹\n");
printf(" 2.切换到当前路径的上级文件夹: cd ..\n");
printf(" 3.在任意路径下切换回根路径ROOT: cd \\ \n");
printf(" 4.列出当前路径下的全部文件夹和文件: dir\n");
printf(" 5.在当前路径下新建文件夹: md + 文件夹名\n");
printf(" 6.在当前路径下新建文件: mk + 文件名\n");
printf(" 7.位示图: show\n");
printf(" 8.删除当前路径下的某文件名: del+文件名\n");
printf(" 9.文件分配表:fat\n");
printf(" 10.删除文件夹:rd+文件夹名\n");
printf(" 11.操作文件oper\n");
printf(" ******************************************************\n");
}
void odr(F *root, char *name)
{
if (root != NULL)
{
odr(root->child, name);
{
if (strcmp(root->name, name) == 0)
{
printf("close为关闭文件,r为读文件\n");
char w[8];
scanf("%s", w);
if (strcmp(w, "r") == 0)
{
root->flagg = 1;
printf("%s\n", root->neirong);
}
if (strcmp(w, "close") == 0)
{
root->flagg = 0;
printf("关闭成功\n");
}
}
}
odr(root->next, name);
}
}
void ZT(char *name)
{
F *np = p;
odr(np, name);
}
int main()
{
meum();
char a[6];
char f_name[10];
int flag = 1;
F *f = (F *)malloc(sizeof(F));
strcpy(f->name, "root");
f->type = 2;
f->size = 0;
getTime(f);
f->child = NULL;
f->next = NULL;
f->parent = NULL;
f->first = -2;
p = f; // p指向当前目录
byteCreat();
// head = (node *)malloc(sizeof(node));
// head->next = NULL;
while (flag)
{
do
{
updatelujing();
scanf(" %s", a);
if (strcmp(a, "md") == 0) // 创建空目录
{
makedir(2);
}
else if (strcmp(a, "cd") == 0)
{
// printf("请输入目录名\n");
scanf(" %s", f_name);
cdir(f_name);
}
else if (strcmp(a, "rd") == 0) // 删除空目录
{
// printf("请输入需要删除的目录名\n");
scanf(" %s", f_name);
delDir(f_name);
}
else if (strcmp(a, "mk") == 0) // 创建文件
{
makedir(1);
}
else if (strcmp(a, "del") == 0) // 删除文件
{
// printf("请输入需要删除的文件名\n");
scanf(" %s", f_name);
delFile(f_name);
}
else if (strcmp(a, "dir") == 0) // 列出目录信息
{
printNow();
}
else if (strcmp(a, "q") == 0)
{
flag = 0;
}
else if (strcmp(a, "show") == 0) // 展示位示图
{
byteShow();
}
else if (strcmp(a, "fat") == 0) // 展示fat表
{
fatShow();
}
else if (strcmp(a, "cd..") == 0) // 返回上一级目录
{
p = p->parent;
}
else if (strcmp(a, "oper") == 0)
{
printf("请输入文件名\n");
scanf("%s", f_name);
ZT(f_name);
}
} while (flag == 1);
}
return 0;
}
五、运行结果
1、建立文件夹如下图所示
菜单如下图:
0/1矩阵为位示图,-1/-2为FAT块,记录了文件在位示图的位置,这里为了方便观察,一维数组FAT将以矩阵的形式输出:
2、文件保护
进行读的文件操作,此时文件打开,无法删除文件。需要关闭文件才可以进行删除操作
输入关闭文件的指令,即可删除文件如下图所示