一、实验内容
设计一个简单的二级文件系统。要求做到以下几点:
(1)可以实现下列几条命令(至少4条);
login 用户登陆
dir 列文件目录
create 创建文件
delete 删除文件
open 打开文件
close 关闭文件
read 读文件
write 写文件
(2)列目录时要列出文件名、物理地址、保护码和文件长度;
(3)源文件可以进行读写保护。
二、源码
#include<iostream>
#include<malloc.h>
#include<string.h>
using namespace std ;
//定义文件数据结构
typedef struct file
{
char file_name[20] ;
bool file_protect[3] ;
bool open_file_protect[3] ; //仅在文件打开时有效
int read , write ; //定义为读写指针
int file_length ;
struct file *next ;
} File ;
//用户与文件的映射
typedef struct x_map
{
char userName[20] ;
File *file ;
struct x_map *next ;
} Map ;
//定义主文件目录
typedef struct mfd
{
Map *head , *tail ;
} MFD ;
//打开文件目录
typedef struct afd
{
File *head , *tail ;
int max_open ;
int current_open ;
} AFD ;
//进行用户初始化登录
void login(MFD* mfd)
{
//初始化两个不同用户
for (int i = 1; i <= 2; i++)
{
Map* m;
m = (Map*)malloc(sizeof(Map));
if (m == NULL)
{
exit(0);
}
cout << "请初始化用户名 : ";
cin >> m->userName;
m->file = NULL;
m->next = NULL;
if (mfd->head == NULL)
{
mfd->head = mfd->tail = m;
}
else
{
mfd->tail->next = m;
mfd->tail = m;
}
}
}
//进行用户的查找,找到则返回用户映射指针
Map* quest(char userName[], MFD* mfd)
{
Map* m = NULL;
m = mfd->head;
while (m)
{
if (strcmp(userName, m->userName) == 0)
{
return m;
}
m = m->next;
}
return NULL;
}
//进行文件的创建操作
bool create(Map* user, char file_name[], bool file_protect[3], int file_length)
{
File* file;
file = (File*)malloc(sizeof(File));
if (file == NULL)
{
return false;
}
//进行文件的初始化
strcpy_s(file->file_name, file_name);
file->file_protect[0] = file_protect[0];
file->file_protect[1] = file_protect[1];
file->file_protect[2] = file_protect[2];
file->file_length = file_length;
file->read = file->write = 0;
file->next = NULL;
if (user->file == NULL)
{
user->file = file;
}
else
{
File* op;
op = user->file;
}
}
//进行文件删除操作
bool Delete(Map* user, char file_name[], AFD* afd)
{
File* file = NULL, * prefile = NULL, * temp;
file = afd->head;
//在打开文件中查找
while (file)
{
if (strcmp(file_name, file->file_name) == 0)
{
cout << "\"" << file_name << "\" 现在是打开状态 , 请先关闭 ! \n";
return false;
}
file = file->next;
}
file = user->file;
//在文件中进行查找
while (file)
{
if (strcmp(file_name, file->file_name) == 0)
{
if (file == user->file)
{
temp = file;
user->file = file->next;
}
else
{
temp = file;
prefile->next = file->next;
}
delete temp;
return true;
}
prefile = file;
file = file->next;
}
if (prefile->next == NULL)
{
cout << "用户 " << user->userName << " 不存在文件 \"" << file_name << "\"" << endl;
}
return false;
}
//进行文件打开操作
bool open(Map* user, char file_name[], AFD* afd, bool open_file_protect[])
{
File* file = NULL;
file = user->file;
while (file)
{
if (strcmp(file->file_name, file_name) == 0)
{
break;
}
file = file->next;
}
if (file)
{
File* xfile;
xfile = (File*)malloc(sizeof(File));
if (xfile == NULL)
{
return false;
}
*xfile = *file;
//根据文件的权限进行打开权限的赋值
if (xfile->file_protect[0] >= open_file_protect[0])
{
xfile->open_file_protect[0] = open_file_protect[0];
}
else
{
cout << "禁止读操作 !" << endl;
return false;
}
if (xfile->file_protect[1] >= open_file_protect[1])
{
xfile->open_file_protect[1] = open_file_protect[1];
}
else
{
cout << "禁止写操作 ! " << endl;
return false;
}
if (xfile->file_protect[2] >= open_file_protect[2])
{
xfile->open_file_protect[2] = open_file_protect[2];
}
else
{
cout << "禁止读写操作 ! " << endl;
return false;
}
xfile->next = NULL;
if (afd->head == NULL)
{
afd->head = afd->tail = xfile;
afd->current_open += 1;
}
else if (afd->current_open < afd->max_open)
{
afd->tail->next = xfile;
afd->tail = xfile;
afd->current_open += 1;
}
else
{
cout << "打开的文件数量太多 ! " << endl;
return false;
}
}
else
{
cout << "文件 " << file_name << " 不存在 !" << endl;
return false;
}
}
//关闭文件
bool close(AFD* afd, char file_name[])
{
File* file = NULL, * preFile = NULL, * temp = NULL;
//在打开文件链表中进行查找
file = afd->head;
while (file)
{
if (strcmp(file->file_name, file_name) == 0)
{
if (file == afd->head)
{
if (file == afd->tail)
{
temp = file;
afd->head = afd->tail = NULL;
}
else
{
temp = file;
afd->head = file->next;
}
}
else if (file == afd->tail)
{
temp = file;
preFile->next = NULL;
afd->tail = preFile;
}
else
{
temp = file;
preFile->next = file->next;
}
delete temp;
return true;
}
preFile = file;
file = file->next;
}
cout << "文件不存在 ! " << endl;
return false;
}
int main()
{
MFD *mfd ;
mfd = (MFD*)malloc(sizeof(MFD)) ;
if(mfd == NULL)
{
exit(0) ;
}
mfd->head = mfd->tail = NULL ;
login(mfd) ;
char userName[20] ;
while (true)
{
cout << "请选择用户登录 : ";
cin >> userName;
Map* user;
user = quest(userName, mfd);
if (user == NULL)
{
cout << "没有此用户! " << endl;
}
else
{
//为用户初始化打开文件目录
AFD* afd;
afd = (AFD*)malloc(sizeof(AFD));
if (afd == NULL)
{
cout << "内存空间不足 ! " << endl;
exit(0);
}
afd->head = afd->tail = NULL;
afd->max_open = 5;
afd->current_open = 0;
char command[20];
char file_name[20];
bool file_protect[3];
bool open_file_protect[3];
int file_length;
while (true)
{
cout << userName << ">>";
cin >> command;
//输入命令进行操作
if (strcmp(command, "create") == 0)
{
cout << "请输入你想创建的文件名 : ";
cin >> file_name >> file_protect[0] >> file_protect[1] >> file_protect[2] >> file_length;
create(user, file_name, file_protect, file_length);
}
else if (strcmp(command, "delete") == 0)
{
cout << "请输入你想删除的文件名 : ";
cin >> file_name;
Delete(user, file_name, afd);
}
else if (strcmp(command, "open") == 0)
{
cout << "请输入你想打开的文件名 : ";
cin >> file_name >> open_file_protect[0] >> open_file_protect[1] >> open_file_protect[2];
open(user, file_name, afd, open_file_protect);
}
else if (strcmp(command, "close") == 0)
{
cout << "请输入你想关闭的文件名 : ";
cin >> file_name;
close(afd, file_name);
}
else if (strcmp(command, "exit") == 0)
{
break;
}
else
{
cout << "不存在此命令 \"" << command << "\"" << endl;
}
}
}
}
return 0 ;
}
三、总体设计
此次我选择了 login用户登陆,create创建文件,delete删除文件,open打开文件,close关闭文件这五条指令进行实验。用户登录处可以进行多个用户的初始化及登录选择。
- 封装所需数据结构
- 定义文件数据结构
一个文件需要包含文件名(file_name)作为标识作用,文件长度(file_length),文件保护位(file_protect)譬如文件是只读文件、只写文件或是可读也可写文件,在读写操作中十分关键,决定了一个文件是否可以进行读写操作,虽然在我此次实验中不涉及读写操作,但为了其文件结构的完整性,依然选择添加。有了文件保护位,可以在进行读写指针(read, write)的设置,方便读写操作。最后在设置一个文件指针(next)以描述各文件之间的关系。
- 定义用户与文件的映射
由于是多用户的文件系统,每一个用户都有自己拥有的文件,为了能够快速准确找到用户对于的文件,我们建立一个用户与文件的映射。其中包含用户名(userName),文件(file),此时的文件是所属映射中的用户。而一个用户又可以拥有多个文件,所以再设置一个同类型的映射指针(next)用于一个用户与多文件映射之间进行联系。
- 定义主文件目录
用户有多个文件,文件之间形成目录,创建好目录之后才能方便管理和显示。在结构体中设置头指针和尾指针(head, tail)。
- 定义打开文件目录
由于删除文件仅能删除关闭的文件,处于打开状态的文件不可以删除。这是我们就需要文件打开目录来记录已打开的文件。但必须限制文件打开的数量,不能无限去打开文件。故设置一个最大打开数量(max_open),同样需要在结构体中设置头指针和尾指针(head, tail),因为有多个打开文件需要进行联系。
- login用户登陆
用户登录包括两个部分:初始化多个个不同用户,进行用户的查找
- 初始化多个个不同用户(login)
使用 (Map*)malloc(sizeof(Map))动态开辟用户与文件的映射(即代表用户)
开辟多个用户可以设置一个整型变量num用于记录用户个数,利用整型变量num控制循环次数,在每次循环中都为用户输入用户名。并建立用户之间的联系。
- 进行用户的查找(quest)
多用户管理系统要对用户登录时的用户进行校验,看是否存在此用户。用户存在用户与文件的映射m中。通过strcmp(userName, m->userName比对我们所输入用户名是否存在,利用循环进行多次判断,直至m为空仍没找到则返回flase。
- create创建文件
先为文件的创建开辟空间,内存空间不足开辟失败,则直接返回flase。若成功开辟则继续执行,进行文件的初始化。
使用strcpy_s(file->file_name,file_name)语句进行字符串拷贝对文件名进行初始化。最后建立文件与文件之间的联系。
- delete删除文件
删除文件之间先要进行额外的判断,判断文件是否处于打开状态。打开状态下的文件不可删除。在打开文件中通过循环遍历,使用strcmp(file_name, file->file_name) == 0语句判断在打开文件中是否存在。如果存在,则直接使用cout << "\"" << file_name << "\" 现在是打开状态 , 请先关闭 ! \n"语句输出文件是打开状态以提示用户并提前返回flase。若直至file为空仍未找到代表文件不存在或者处于关闭状态,可进入下一步判断。进入文件目录中以文件名作为索引寻找,若找到则改变指针关系,删除文件并返回ture。若直至文件目录为空还未找到则代表文件不存在。通过输出语句输出文件不存在以提示用户并返回flase。
- open打开文件
打开文件主要进行的就是将需要打开的文件链接到打开文件目录中。但在这之前需要进行至少两个判断,第一文件是否存在,第二文件是否已经处于打开状态。第一个判断与之前的判断文件是否存在一样,进入文件目录查找比对。如果文件不存在则通过输出语句输出文件不存在以提示用户并返回flase。如果文件存在则进行第二个判断,遍历文件打开目录,如果文件存在即已打开则返回ture,不进行链接到打开文件目录表中。如果文件不存在即未打开则将文件链接到打开文件目录表中并返回ture。
- close关闭文件
关闭文件主要是将文件从文件打开目录中删除。同样其需要进行两个判断,第一文件是否存在,第二文件是否已经处于打开状态,因为只有打开状态下的文件才需关闭。第一个判断与之前的判断文件是否存在一样,进入文件目录查找比对。如果文件不存在则通过输出语句输出文件不存在以提示用户并返回flase。如果文件存在则进行第二个判断,遍历文件打开目录,如果文件存在即已打开则返回ture,改变指针关系将文件从文件打开目录中删除。如果文件不存在即文件是关闭状态不需要重复关闭直接并返回ture。
(注:文件删除是从文件目录中将文件删除,文件关闭是将文件从文件打开目录中将文件删除,文件起始还存在与文件目录中。)
四、测试及结果分析
- 为方便调试,多用户文件管理系统我们仅初始化两个用户
- 输入两个用户,c和d
- 选择用户e登录提示“没有此用户”
- 选择用户c登录,进入用户c进行如下操作。
第一步创建file0,file1,file2;
第二步打开file0,file1,file2,file7,file7不在文件目录中,故提示file7文件不存在;
第三步:关闭file1
第四步:删除file0,file1,file2,其中file0,而file2处于打开状态不得删除。
5、选择用户d登录,进入用户d进行操作,与用户c类似。
第一步创建dictory5,dictory6,dictory7;
第二步打开dictory5,dictory6,dictory7,dictory1,而dictory1不在文件目录中,故提示dictory1文件不存在;
第三步:关闭dictory6
第四步:删除dictory5,dictory6,dictory7,其中dictory5,dictory7,处于打开状态不得删除。