操作系统课程设计——文件管理模拟 C++版

参考博客:【操作系统之模拟文件管理系统 - CSDN App】

并在此基础上进行了修改和完善部分代码

这是我的第一篇CSDN,内容有点长


实验内容

文件管理

一、目的

通过模拟磁盘,完成操作系统的文件管理功能,掌握包括目录结构的管理、外存空【操作系统之模拟文件管理系统 - CSDN App】间的分配与释放以及空闲空间管理三部分。为写入模拟磁盘的数据文件建立目录,目录可以是单级文件目录、双级文件目录、树形结构目录。在目录中选择某个文件可以将其数据读入模拟内存。

二、设计内容

  1. 通过初始化操作建立一个模拟磁盘,在模拟磁盘中保存目录和文件内容。创建该模拟磁盘时可创建初始的根目录内容、文件分配表。
  2. 文件目录项(可以采用FCB格式)应包括文件名、类型(目录 or文件)、创建日期、大小、第一个磁盘块块号。
  3. 目录管理需支持:
    新建目录:在目录中新建空目录; 删除目录:删除空目录
    为文件建立目录项:一个文件创建成功后,为该文件创建目录项,并将文件和相关信息写入目录;
    删除文件:删除目录中某个文件,删除其在磁盘中的数据,并删除目录项。如果被删除文件已经读入内存应阻止删除,完成基本的文件保护。

三、 设计要求

  1. 不同的功能使用不同的函数实现(模块化),对每个函数的功能和调用接口要注释清楚。对程序其它部分也进行必要的注释。
  2. 对系统进行功能模块分析、画出总流程图和各模块流程图。
  3. 用户界面要求使用方便、简洁明了、美观大方、格式统一。所有功能可以反复使用,最好使用菜单。
  4. 通过命令行相应选项能直接进入某个相应菜单选项的功能模块。
  5. 所有程序需调试通过。

实验说明

本实验是参考了http://t.csdnimg.cn/B0Fqh
 ,并在此基础上进行了修改和完善部分代码。

本次实验为Linux系统设计了一个简单的二级文件系统,可以做到以下几点:

  1. 可用命令及说明
    • register 用户的注册
    • login 用户的登入
    • cd 切换目录
    • mkdir 创建用户的子目录
    • rmdir 删除用户的子目录
    • dir 显示当前目录的子目录
    • create 创建文件夹的文件
    • del 删除文件夹的文件
    • open 打开文件
    • close 关闭文件
    • read 读文件
    • write 写文件
    • set 设置文件的保护码
    • exit 退出系统
    • look 查看系统中的所有用户
    • fatused 查看目前块的状态
  2. 用户登入时会对用户名和密码进行匹配
  3. 进入系统的第一步需要注册账号并登入(为测试方便,初始化了一个用户)
  4. 在创建子目录时必须先登入
  5. 不可在打开的状态下进行任何删除操作
  6. 删除子目录或文件时释放其空间
  7. 创建文件和打开文件时必须先进入文件夹
  8. 文件被打开后可进行读写操作
  9. 文件保护码为2可读可写,为0不可读,其他不可写

文件结构

这些图表是我的一点理解,可能存在偏差

用户文件结构

打开文件结构

创建文件流程图

删除文件流程图

代码说明

整体结构

#define BLOCK_SIZE     		512  //每一个磁盘块的大小
#define BLOCK_NUM       	64   //磁盘块数量
#define MAX_FILE_NUM		16   //每个文件夹的最大文件数
#define MAX_FOLDER_NUM  	16	 //每个用户可以拥有的最大文件夹数
#define MAX_OPEN_FILE_NUM   16	 //最大同时打开文件数
#define MAX_USER_NUM		16   //最大用户数

  • 主目录
    • 存放用户数据结构 最大可存放16个用户
    • 用户名
    • 密码
    • 指向用户的指针
struct MFD//用户数据结构
{
string username;//用户名
string password;//密码
struct USER_UFD *next;//指向用户文件的指针
};

  • 子目录(文件夹数据结构)

    • 各个用户所拥有的目录
    • 最大创建16个目录(及文件)
    • 其中包含文件数据结构
    • 文件夹名字
    • 当前文件夹的文件数
  • 文件数据结构

    • 文件名(同文件下不可重名)
    • 文件保护码(默认为2 可读可写,为0 不可读)
    • 文件长度(长度超过块的存储范围会找下一个块)
    • 文件起始地址
    • 文件创建日期
    • 是否占用
struct UFD//文件夹数据结构 
{
	struct file_message//文件数据结构
	{
		string filename;//文件名
		int protect_code;//文件保护码
		int length;//文件长度
		int addr;//文件起始地址
		string time;	//日期时间,格式为yyyymmdd hhmmss
        int used;//是否被占用 0:空闲 1:占用
	}ufd[MAX_FILE_NUM];//一个文件夹最对可以有16个文件
	string directname;//文件夹名字
	int cur_file_size = 0;//当前该文件夹含有的文件数量
};

  • 打开的文件数据结构
    • 最多打开16个文件
    • 文件名字
    • 文件长度
    • 文件保护码
    • 文件起始地址
    • 当前打开的文件数量
struct UOF//打开文件数据结构
{
	struct uof
	{
		string filename;//文件名字
		int pointer;//文件长度
		int protect_code;//文件保护码  默认2  2为可读可写  0为不可读  其余数字不可写
		int addr;//文件起始地址
	}uof[MAX_OPEN_FILE_NUM];//最多可以同时打开16个文
	int cur_openfilesize = 0;//当前打开的文件数量
};

  • 块号数据结构
    • 存放下一页的块号
    • 块号的状态 (1 占用; 0 空闲)
struct fat//块号
{
	int next = -1;//存放下一页的块号,无为-1
	int used = 0;  //used存在三种状态,1:被占用 0:空闲
}fat[BLOCK_NUM];//最大为64

  • 用户文件数据结构
    • 包含文件夹数据结构
    • 当前拥有的文件夹数量
struct USER_UFD//用户文件数据结构
{
	struct UFD direct[MAX_FOLDER_NUM];//一个用户最多可以有16个文件夹
	int cur_user_direct_size = 0;//当前拥有的文件夹数量
};

定义属性及函数声明

int mark = 1;
struct USER_UFD cur_user_all_direct_array[MAX_USER_NUM];//用户文件数组 16
struct MFD cur_user;//当前用户
struct UOF * cur_opentable;//指向打开文件数组的指针
char *fdisk;//模拟磁盘指针
struct UOF openfile[MAX_OPEN_FILE_NUM];//每一个用户对应一个文件打开表对象,16
struct MFD mfd[MAX_USER_NUM]; //16个用户
int cur_user_size = 0;//记录当前用户的人数
string path;

int REGISTER();//注册用户
int LOGIN();//登录用户
int CREATE(string name);//创建文件
int DELETE(string name);//删除文件
int OPEN(string name);//打开文件
int WRITE(string name);//写文件
int READ(string name);//读文件
int CLOSE(string name);//关闭文件
int CD();//更改当前目录
int MKDIR(string name);//创建文件夹
int DIR();//显示当前目录的子目录
int SET(string name, int protectcode);//设置保护码
void INPUT_OPERATION();//指令输入

main函数

初始化一个用户方便调试代码
并输出命令格式说明

int main()
{
	cur_user.username = "";
	path = "";
	fdisk = (char *)malloc(1024 * 1024 * sizeof(int));//模拟硬盘指针
	
	//注册root用户并登录,方便调试代码
	mfd[cur_user_size].username = "root";
	mfd[cur_user_size].password = "123456";
	cur_user_size++;
	mfd[0].next = &(cur_user_all_direct_array[0]);	
	cur_user = mfd[0];
	cur_user.next->cur_user_direct_size = mfd[0].next->cur_user_direct_size;
	cur_user_size++;
	cur_opentable = &openfile[cur_user_size]; //指针指向文件打开表对象
	cur_opentable->cur_openfilesize = 0;
	
	cout << "*******************欢迎使用二级文件系统*******************" << endl;
	cout << "        命令格式                说明                      " << endl;
	cout << "        register                注册用户                  " << endl;
	cout << "        login                   登录                      " << endl;
	cout << "        cd 目录名               更改当前目录              " << endl;
	cout << "        mkdir 目录名            创建子目录                " << endl;
	cout << "        rmdir 目录名            删除子目录                " << endl;
	cout << "        dir                     显示当前目录的子目录      " << endl;
	cout << "        create 文件名           创建文件                  " << endl;
	cout << "        del 文件名              删除文件                  " << endl;
	cout << "        open 文件名             打开文件                  " << endl;		
	cout << "        close                   关闭文件                  " << endl;
	cout << "        read 文件名               读文件                    " << endl;
	cout << "        write 文件名              写文件                    " << endl;
	cout << "        set 文件名 文件保护码   设置文件保护码            " << endl;
	cout << "        exit                    退出系统                  " << endl;
	cout << "        look                    查看用户                 " << endl;
	cout << "        fatused                 查看块                 " << endl;
	while (mark)
		INPUT_OPERATION();
	free(fdisk);
	return 1;
}

INPUT_OPERATION函数(指令输入)

开始运行文件管理系统时是未登入的状态(现在代码是默认初始化一个用户)
未登录时显示localhost :
登入后显示cur_user.username@localhost home/ path:(用户名+@localhost home/: )刚登入时path=""
然后通过cin >> operaton;输入控制语句,进行匹配调用不同的函数

void INPUT_OPERATION()//指令输入
{
	if (cur_user.username == "")
		cout << "localhost :";
	else
		cout << cur_user.username << "@localhost  home/" << path << ":";
	string operaton;
	cin >> operaton;
	if (operaton == "login")
	{
		LOGIN();
	}
	else if (operaton == "create")
	{
		string filename;
		cin >> filename;
		CREATE(filename);
	}
	else if (operaton == "del")
	{
		string name;
		cin >> name;
		DELETE(name, -1);
	}
	else if (operaton == "open")
	{
		string name;
		cin >> name;
		OPEN(name);
	}
	else if (operaton == "close")
	{
		string name;
		cin >> name;
		CLOSE(name);
	}
	else if (operaton == "write")
	{
		string content;
		string name;
		cin >> name;
		WRITE(name);
	}
	else if (operaton == "read")
	{
		string name;
		cin >> name;
		READ(name);
	}
	else if (operaton == "exit")
	{
		exit(0);
	}
	else if (operaton == "cd")
	{
	CD();
	}
	else if (operaton == "dir")
	{
		DIR();
	}
	else if (operaton == "mkdir")
	{
		string name;
		cin >> name;
		MKDIR(name);
	}
	else if (operaton == "register")
	{
		REGISTER();
	}
	else if (operaton == "exit")
	{
		mark = 0;
	}
	else if (operaton == "set")
	{
		string name;
		int protextcode;
		cin >> name;
		cin >> protextcode;
		SET(name, protextcode);
	}
	else if (operaton == "rmdir")
	{
		string name;
		cin >> name;
		RMDIR(name);
	}
	else if (operaton == "look")
	{
		LOOK();
	}
	else if (operaton == "fatused")
	{
		fatused();
	}
	else
	{
		cout << "命令错误,请重新输入" << endl;
	}
}

REGISTER函数(用户注册)

输入用户名和密码
先对比用户数据结构数组(struct UOF openfile[MAX_OPEN_FILE_NUM])中的用户名,用户名重复则失败,否则成功,并将用户信息存入用户结构体中

int REGISTER()//注册用户
{
	cout << "请输入用户名:";
	string username;
	cin >> username;
	cout << "请输入密码:";
	string password;
	cin >> password;
	for (int i = 0; i < MAX_USER_NUM; i++)//最大可有16个用户
	{
		if (mfd[i].username == username)
		{
			cout << "注册失败,该用户名已存在" << endl;
			return 0;
		}
	}
	//将用户信息存入用户结构体中
	mfd[cur_user_size].username = username;
	mfd[cur_user_size].password = password;
	cur_user_size++;
	cout << "注册成功" << endl;
	return 1;
}

LOGIN函数(用户登入)

把输入的用户名和密码与记录所有用户的结构体数组进行比对,
比对成功后将此用户文件的指针加入到当前用户文件数组中,并设置此为当前目录(即当前用户下)
并指向当前用户的文件打开表对象

int LOGIN()//用户登入
{
	cout << "请输入用户名:";
	string username;
	cin >> username;
	cout << "请输入密码:";
	string password;
	cin >> password;
	int i;
	for (i = 0; i < cur_user_size; i++)
	{
		if (mfd[i].username == username)
		{
			if (mfd[i].password != password)
			{
				cout << "密码错误" << endl;
				return 0;
			}
			else
			{
				break;
			}
		}
	}
	if (i == cur_user_size)
	{
		cout << "没有此用户" << endl;
		return 0;
	}
	mfd[i].next = &(cur_user_all_direct_array[i]);//用户文件指向第i个用户数组
	cur_user = mfd[i];//登入后此为当前目录
	cur_user.next->cur_user_direct_size = mfd[i].next->cur_user_direct_size;//让当前用户找到所登入的用户文件
	cur_user_size++;//用户人数+1
	cur_opentable = &openfile[cur_user_size]; //指针指向文件打开表对象  每一个用户一个文件打开表对象,该表中最多存放16个文件夹
	cur_opentable->cur_openfilesize = 0;//设置当前打开的文件数为0
	path = "";//设置path在刚登入时为空
	return 1;
}

LOOK函数(查看所有用户)

遍历用户名,方便观察调试

//查看用户表
int LOOK()
{
	int i;
	for (i = 0; i < cur_user_size; i++)
	{
		cout << mfd[i].username << "  ";//遍历现有的用户数,方便观察
	}
	printf("\n"); 
	return 1;
}

getCurrentTimeStr函数(获取当前时间)

string getCurrentTimeStr()//得到时间
{
	time_t t = time(NULL);
	char ch[64] = {0};
	strftime(ch, sizeof(ch) - 1, "%Y-%m-%d %H:%M:%S", localtime(&t));
	return ch;
}

CREATE函数(创建文件)

  1. 必须先进入文件夹在创建文件
  2. 找到对应文件夹
  3. 判断文件是否重名
  4. 判断该文件夹中的文件是否满16个
  5. 判断是否有空闲块
  6. 条件满足创建,否则输出提示

更新文件数据,文件数量+1,磁盘块占用

//创建文件
int CREATE(string name)
{
	int index;
	if (path == "")
	{
		cout << "请先进入文件夹,再创建文件" << endl;
		return 0;
	}
	for (index = 0; index < cur_user.next->cur_user_direct_size; index++)
	{
		if (path == cur_user.next->direct[index].directname)//如果path等于文件夹名
		{
			break;
		}
	}
	int i;
	for (i = 0; i < cur_user.next->direct[index].cur_file_size; i++)//循环当前文件夹的文件数
	{
		if (name == cur_user.next->direct[index].ufd[i].filename)
		{
		break;//文件名重复跳出循环
		}
	}
	if (i < cur_user.next->direct[index].cur_file_size)//重复 i会小于文件数,无重复 则等于
	{
		cout << "文件名重复" << endl;
		return 0;
	}
	if (cur_user.next->direct[index].cur_file_size == MAX_OPEN_FILE_NUM)
	{
		cout << "该文件夹文件已达到"<< MAX_OPEN_FILE_NUM << "个" << endl;
		return 0;
	}
	int j;
	for (j = 0; j < BLOCK_NUM; j++)//判断是否有空闲块 磁盘块数量64 每个块大小512
	{
		if (fat[j].used == 0)
		{
			break;
		}
			
	}
	if (j >= BLOCK_NUM)
	{
		cout << "磁盘没有空闲块了" << endl;
		return 0;
	}

	string tim=getCurrentTimeStr();
	cout << tim << endl;
	

	cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size].time = tim;//创建时间
	cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size].filename = name;//上面都通过了保存文件名
	cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size].addr = j; //文件起始盘块号
	cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size].length = 0;//未写入数据,长度为0
	cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size].protect_code = 2; //表示可读可写
	//cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size].time = tim;//创建时间
	cur_user.next->direct[index].cur_file_size++;//用户文件数量加1
    fat[j].used = 1;//此时已被占用
	return 1;
}

DELETE函数(删除文件)

  1. 判断是否在文件中
  2. 循环找到文件夹
  3. 循环找到文件
  4. 判断文件是否打开(打开时文件不可删除)
  5. 删除文件或输出提示

更新文件数组信息,将最后一个文件替换到删除的文件中
如果已经是最后一个文件,则直接删除

//删除文件
int DELETE(string name, int mark)
{
	if (path == "" && mark == -1)
	{
		cout << "请在文件夹中删除文件" << endl;
		return 0;
	}
	int index;
	if(mark != -1) 	//如果是被RMDIR函数调用,则当前path=""
		index = mark;
	else
		for (index = 0; index < cur_user.next->cur_user_direct_size; index++)//循环用户拥有的文件夹
		{
			if (path == cur_user.next->direct[index].directname)//找到文件夹后退出
			{
				break;
			}
		}

	int i;
	for (i = 0; i < cur_user.next->direct[index].cur_file_size; i++)//循环该文件夹的文件
	{
		if (cur_user.next->direct[index].ufd[i].filename == name)//找到文件后退出
		{
			break;
		}
	}
	if (i >= cur_user.next->direct[index].cur_file_size)
	{
		cout << "没有该文件" << endl;
		return 0;
	}
	int j;
	for (j = 0; i < cur_opentable->cur_openfilesize; j++)//循环打开的文件,查看是否被打开
	{
		if (cur_opentable->uof[j].filename == name)
			break;
	}
	if (j < cur_opentable->cur_openfilesize)
	{
		cout << "该文件已被打开,无法删除" << endl;
		return 0;
	}

	//更新当前用户目录下文件数组信息,就是将最后一个文件的信息替换到要删除的文件的位置

	//如果本身是最后一个文件,那么不需要替换,直接删除
	if(i == cur_user.next->direct[index].cur_file_size -1)
	{
		fat[cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size - 1].addr].used = 0; 
		cur_user.next->direct[index].cur_file_size--;
		//next为-1 说明没有下一页,此为最后一页
		int temp = fat[cur_user.next->direct[index].ufd[i].addr].next;
		while (temp != -1)//删除该文件的内存空间
		{
			fat[temp].used = 0;
			temp = fat[temp].next;
		}
		cout << "删除文件" << name << "成功" << endl;
		return 1;
	}
	else
	{
		cur_user.next->direct[index].ufd[i].filename = cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size - 1].filename;
		//先将该块置0
		fat[cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size - 1].addr].used = 0; 
		cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size - 1].addr = cur_user.next->direct[index].ufd[i].addr;
		cur_user.next->direct[index].ufd[i].length = cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size - 1].length;
		cur_user.next->direct[index].ufd[i].protect_code = cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size - 1].protect_code;
		cur_user.next->direct[index].cur_file_size--;

		//将该文件设置为占用
		fat[cur_user.next->direct[index].ufd[i].addr].used = 1;
		
		//next为-1 说明没有下一页,此为最后一页
		int temp = fat[cur_user.next->direct[index].ufd[i].addr].next;
		while (temp != -1)//删除该文件的内存空间
		{
			fat[temp].used = 0;
			temp = fat[temp].next;
		}
		cout << "删除文件" << name << "成功" << endl;
		return 1;
	}
}

OPEM函数(打开文件)

  1. 判断是否在文件夹中
  2. 循环文件夹数,找到要打开文件所在的文件夹
  3. 循环文件数,找到文件
  4. 判断打开的文件数是否满16
  5. 判断文件是否已经打开
  6. 将文件信息加入到已打开的文件数组中
  7. 打开文件

int OPEN(string name)//打开文件
{
    if (path == "")
	{
		cout << "请在文件夹中打开文件" << endl;
		return 0;
	}
	string AllName = path +'/'+name;//设置该变量的原因是防止出现打开文件数组中存在文件夹名不同而文件同名的情况
	int index;
	for (index = 0; index < cur_user.next->cur_user_direct_size; index++)//循环当前用户的文件
	{
		if (path == cur_user.next->direct[index].directname)
		{
			 break;
		}
	}
	int i = 0;
	for (i = 0; i < cur_user.next->direct[index].cur_file_size; i++)//循环文件数
	{
		if (name == cur_user.next->direct[index].ufd[i].filename)//找到同名文件
			 break;
	}
	if (i >= cur_user.next->direct[index].cur_file_size)
	{
		cout << "没有该文件" << endl;
		return 0;
	}
	if (cur_opentable->cur_openfilesize == MAX_OPEN_FILE_NUM)
	{
		cout << "文件打开数量已经达到最大值" << endl;
		return 0;
	}
	
	for (int j = 0; i < cur_opentable->cur_openfilesize; j++)
	{
		if (cur_opentable->uof[i].filename == AllName)//在打开文件中找到相应文件夹中的文件
		{
			 cout << "该文件已经打开" << endl;
			 return 0;
		}
	}
	int k;
	for (k = 0; k < cur_user.next->direct[index].cur_file_size; k++)
	{
		if (cur_user.next->direct[index].ufd[k].filename == name)//找到相应文件夹中的文件
		 break;
	} 
	//将该文件的信息加入到已打开的文件数组中
	cur_opentable->uof[cur_opentable->cur_openfilesize].filename = AllName;//在打开的文件数组中加入该文件
	cur_opentable->uof[cur_opentable->cur_openfilesize].protect_code = 	cur_user.next->direct[index].ufd[k].protect_code;//保护码
	cur_opentable->uof[cur_opentable->cur_openfilesize].pointer = cur_user.next->direct[index].ufd[k].length;
	cur_opentable->uof[cur_opentable->cur_openfilesize].addr = cur_user.next->direct[index].ufd[k].addr;//起始地址
	cur_opentable->cur_openfilesize++; //文件打开数量加1
	cout << "文件打开成功" <<endl;
	return k; //返回文件在文件打开表中的第几项
}

CLOSE函数(关闭文件)

  1. 循环打开的文件数,找到文件
  2. 更新打开文件数组信息将最后一个打开文件的信息替换到要关闭的位置
  3. 关闭文件

int CLOSE(string name)//关闭文件
{	
	int i;
	for (i = 0; i < cur_opentable->cur_openfilesize; i++)
	{
		if (cur_opentable->uof[i].filename == path + '/' + name)
		{
			break;
		}
	}
	if (i >= cur_opentable->cur_openfilesize)
	{
		cout << "该文将未打开" << endl;
		return 0;
	}
	//更新当前用户打开文件数组信息,就是将最后一个打开文件的信息替换到要关闭的打开文件的位置
	cur_opentable->uof[i].filename = cur_opentable->uof[cur_opentable->cur_openfilesize - 1].filename;
	cur_opentable->uof[i].pointer = cur_opentable->uof[cur_opentable->cur_openfilesize - 1].pointer;
	cur_opentable->uof[i].protect_code = cur_opentable->uof[cur_opentable->cur_openfilesize - 1].protect_code;
	cur_opentable->uof[i].addr = cur_opentable->uof[cur_opentable->cur_openfilesize - 1].addr;
	cur_opentable->cur_openfilesize--;
	cout << "文件关闭成功" << endl;
	return 1;
}

WRITE函数(写入文件)

  1. 循环文件夹数,找到文件所在文件夹
  2. 循环打开文件,判断文件是否已打开
  3. 判断文件保护码是否可写
  4. 输入内容
  5. 更新磁盘空间大小
  6. 磁盘空间不够写,填满最后一个磁盘块后寻找下一个空闲块
  7. 写入
int WRITE(string name)//写入文件
{
	int index; //标识当前目录在direct数组中第几个
	for (index = 0; index < cur_user.next->cur_user_direct_size; index++)
	{
		if (path == cur_user.next->direct[index].directname)//在当前用户的文件夹中找到相应的文件
		{
			break;
		}
	}
	int i;
	//判读文件是否打开
	for (i = 0; i < cur_opentable->cur_openfilesize; i++)
	{
		if (cur_opentable->uof[i].filename == path + '/' + name)//循环打开的文件
			break;
	}
	if (i >= cur_opentable->cur_openfilesize)
	{
		cout << "文件没有打开, 无法写入" << endl;
		return -1;
	}
	int fd = i; //获取文件描述字
	//判断读文件的合法性
	if (cur_opentable->uof[fd].protect_code != 2)
	{
		cout << "文件不可写" << endl;
		return -1;
	}
	else
	{
		string content;//字符串  存储输入的一行内容
		cin.ignore();
		//cin.ignore()函数是C++标准输入流(cin)中的一个方法。cin.ignore()函数中有两个参数,
		//分别为数值型的a 和 字符型的 ch ,即cin.ignore( a, ch )。它表示从输入流 cin 中提取
		//字符,提取的字符被忽略,不被使用。而每抛弃一个字符,它都要进行计数和比较字符:如果
		//计数值达到 a 或者被抛弃的字符是 ch ,则cin.ignore() 函数执行终止;否则,它继续等待。
		//如果默认不给参数的话,默认参数为cin.ignore(1, EOF),即把EOF前的1个字符清掉,没有遇
		//到EOF就清掉一个字符然后结束。
		cout << "请输入文件要写入的内容: " << endl;;
		getline(cin, content); //读入一整行内容
		int cin_len = content.length();//输入一行的长度
		int cin_p=0;//初值为0
		int last_block_res;//磁盘剩余空间的大小

		//追加写
		//找到该文件存放的最后一个磁盘块  
		int last_block = cur_opentable->uof[fd].addr;
		while (fat[last_block].next != -1)
		{
			last_block = fat[last_block].next;
		}

		int temp;//保存当前所写的文件在用户文件目录表的第几项,为了后面修改文件的大小
		for (int k = 0; k < cur_user.next->direct[index].cur_file_size; k++)
		{
			if (cur_user.next->direct[index].ufd[k].filename == name)//找到该文件
			{
				temp = k;
				break;
			}
		}

		//计算该文件存放的最后一个地址
		char  * first;
		first = fdisk + last_block * BLOCK_SIZE + cur_opentable->uof[fd].pointer % BLOCK_SIZE;

		//计算最后一个磁盘块还有多少空间
		if(cur_opentable->uof[fd].pointer == 0)
			last_block_res = BLOCK_SIZE;
		else if(cur_opentable->uof[fd].pointer % BLOCK_SIZE == 0)
			last_block_res = 0;
		else
			last_block_res = BLOCK_SIZE - (cur_opentable->uof[fd].pointer % BLOCK_SIZE);

		//首先填满最后一个磁盘块
		if(last_block_res)
		{
			if(last_block_res <= cin_len)	//当最后一个磁盘块的空间小于输入内容大小
			{
				for(int j=0;j<last_block_res;j++)
					first[j] = content[cin_p++];
				cin_len -= last_block_res;//计算还有多少字符没存入
			}
			else	//当最后一个磁盘块的空间大于输入内容大小
			{
				for(int j=0;j<cin_len;j++)
					first[j] = content[cin_p++];
				cur_opentable->uof[fd].pointer = cur_opentable->uof[fd	].pointer + cin_len;  //更新文件打开表
				cur_user.next->direct[index].ufd[temp].length = cur_user.next->direct[index].ufd[temp].length + cin_len; //更新用户目录文件表
				cout << "文件写入成功,共写入" << cin_len << "字节" << endl;
				return 0;
			}
		}

		//计算还需要多少个磁盘块,判断空间是否足够
		int times = cin_len / BLOCK_SIZE;//向上取整 求存满块数
		int offset = cin_len % BLOCK_SIZE;//取余  未存满
		if (offset != 0)
			times++;//块数+1

		int unused_block_num = 0; //记录没有使用过的磁盘块的个数
		int flag = 0;
		for (int j = 0; j < BLOCK_NUM; j++)
		{
			if (fat[j].used == 0)
			{
				unused_block_num++;
			}
		}

		if(unused_block_num < times)//剩余的块不够写
		{
			cout<<"空间不足,写入内容失败!\n";
			return -1;
		}

		int next_block = fat[cur_opentable->uof[fd].addr].next; //记录块的起始地址的next
		int first_unused_block; //记录第一个没有被使用过的磁盘
		for(int j=0;j<times;j++)
		{
			for (int k = 0; k < BLOCK_NUM; k++)
			{
				if (fat[k].used == 0)
				{
					first_unused_block = k;
					break;
				}
			}
			fat[last_block].next = first_unused_block;
			last_block = first_unused_block;
			first = fdisk + last_block * BLOCK_SIZE;
			if(j!=times-1)
			{
				for(int k = 0;k<BLOCK_SIZE;k++)
					first[k] = content[cin_p++];
			}
			else
			{
				for(int k=0;k<offset;k++)
					first[k] = content[cin_p++];
			}

		}
		cur_opentable->uof[fd].pointer = cur_opentable->uof[fd].pointer + cin_p;  //更新文件打开表
		cur_user.next->direct[index].ufd[temp].length = cur_user.next->direct[index].ufd[temp].length + cin_p; //更新用户目录文件表
		cout << "文件写入成功,共写入" << cin_p << "字节" << endl;
		return 0;
	}
}

READ函数(读文件)

  1. 找到文件所在文件夹
  2. 循环文件数,找到文件
  3. 判断文件是否打开
  4. 判断文件是否可读
  5. 借助缓冲区输出文件内容
  6. 释放缓冲区

int READ(string name)//读文件
{
	int index1; //标识当前目录在direct数组中第几个
	for (index1 = 0; index1 < cur_user.next->cur_user_direct_size; index1++)
	{
		if (path == cur_user.next->direct[index1].directname)//找到文件夹
		{
			break;
		}
	}
	int a;
	for (a = 0; a < cur_user.next->direct[index1].cur_file_size; a++)    //判断文件是否存在
	{
	if (cur_user.next->direct[index1].ufd[a].filename == name)//找文件
		break;
	}
	if (a >= cur_user.next->direct[index1].cur_file_size)
	{
		cout << "没有这个文件" << endl;
		return 0;
	}
	int i;
	//判读文件是否打开
	for (i = 0; i < cur_opentable->cur_openfilesize; i++)
	{
		if (cur_opentable->uof[i].filename == path + '/' + name)
			break;
	}
	if (i >= cur_opentable->cur_openfilesize)
	{
		cout << "文件没有打开, 无法读取" << endl;
		return -1;
	}
	int fd = i; //获取文件描述字
	//判断读文件的合法性
	if (cur_opentable->uof[fd].protect_code == 0) //我们创建的文件都是默认可读可写的
	{
		cout << "文件不可读" << endl;
		return 0;
	}
	else
	{
		int len = cur_opentable->uof[fd].pointer; //文件的长度
		int block_num = len / BLOCK_SIZE; //磁盘的个数
		int offset = len % BLOCK_SIZE; //偏移量
		if (offset != 0)
			block_num++;
		
		//如果我用一个文件表示磁盘的引导块,用另一个文件表示磁盘的	数据块,那么我们计算文件的起始位置就不用加上磁盘的引导块了吧
		//关于文件的存放文件,我们char *fdisk表示一整个磁盘,然后不		同文件的内容存放在这个指针所指向的不同字符段
		char * first = fdisk + cur_opentable->uof[fd].addr * BLOCK_SIZE; //文件的起始地址 第0块起始地址为0 第1块起始地址为512
		char * buf = (char *)malloc(513 * sizeof(char)); //缓冲区  给指针申请513个char类型大小的空间  512个空间存放内容 第513个存\0
		cout << "文件的内容为 :";
		for (int k = 0; k < block_num; k++)
		{
			if (k == block_num - 1)  //则是最后一个磁盘块
			{
				int j;
				for (j = 0; j < len - k * BLOCK_SIZE; j++)  //赋值文件剩余的字符,其实就是偏移量
				{
					buf[j] = first[j];
				}
				buf[j] = '\0';//\0结束
				printf("%s\n", buf); //输出文件的内容
			}
			else //不在最后一个磁盘块,也就是在其他已经读满的磁盘块
			{
				int j;
				for (j = 0; j < BLOCK_SIZE; j++)
					buf[j] = first[j]; //缓冲区读满就输出内容
				buf[j] = '\0';
				printf("%s", buf); //输出文件的内容
				int next_block = fat[cur_opentable->uof[fd].addr].next; 				//读完一个磁盘块后,在接着读下一个磁盘块
				first = fdisk + next_block * BLOCK_SIZE;
			}
		}
		cout << endl;
		free(buf); //释放缓冲区
		return 0;
	}
}

CD函数(更改当前目录)

  1. 判断是否在根目录
  2. 循环寻找文件夹
  3. 找到则更改path值

int CD()//更改当前目录
{
	string temp_path;
	cin >> temp_path;
	if (temp_path == "..")
	{
		if(path == "")
		{
			cout << "已在根目录" << endl;
		}
		else
		{
			path = "";
		  	
		}
			return 1;
	}
	int i;
	for (i = 0; i < cur_user.next->cur_user_direct_size; i++)
	{
		if (temp_path == cur_user.next->direct[i].directname)
			break;
	}
	if (i >= cur_user.next->cur_user_direct_size)
	{
		cout << "没有此目录" << endl;
		return 0;
	}
	path = temp_path;//path为该路径
	return 1;
}

MKDIR函数(创建目录)

  1. 判断用户目录是否已达16
  2. 判断目录是否已存在(重名)
  3. 创建目录

int MKDIR(string name)//创建目录
{
	if (cur_user.next->cur_user_direct_size == MAX_USER_NUM)
	{
		cout << "用户目录数量已经达到最大值" << endl;
		return 0;
	}
	int i;
	for (i = 0; i < cur_user.next->cur_user_direct_size; i++)
	{
		if (cur_user.next->direct[i].directname == name)
			break;
	}
	if (i < cur_user.next->cur_user_direct_size)
	{
		cout << "该目录名已存在" << endl;
		return 0;
	}
	cur_user.next->direct[cur_user.next->cur_user_direct_size].directname = name;
	cur_user.next->direct[cur_user.next->cur_user_direct_size].cur_file_size = 0; //新创建的目录里面的文件个数为0
	cur_user.next->cur_user_direct_size++;
	cout << "创建目录成功" << endl;
	return 1;
}

RMDIR函数(删除目录)

  1. 判断是否在根目录
  2. 找到文件夹位置
  3. 判断是否其中有文件打开
  4. 删除该文件夹的所有文件
  5. 将最后一个文件夹的位置替换为删除的文件夹的位置
  6. 目录-1
  7. 成功删除

int RMDIR(string name)//删除目录
{
	if(path != "")
	{
		cout << "请先退出文件夹" << endl;
		return 0;
	}
	int index;//文件夹的位置
	for (index = 0; index < cur_user.next->cur_user_direct_size; index++)//先判断是否有该文件夹
	{
		if (name == cur_user.next->direct[index].directname)
			break;
	}

	for (int i = 0; i < cur_user.next->direct[index].cur_file_size; i++)
	{
		for(int j=0;i<cur_opentable->cur_openfilesize;j++)
			if (name + '/' + cur_user.next->direct[index].ufd[i].filename 	== cur_opentable->uof[j].filename)
			{
				cout << "请先关闭该文件夹的所有文件" << endl;
				return 0;
			}
	}
	//删除该文件夹的所有文件

	for (int i = 0; i < cur_user.next->direct[index].cur_file_size; i++)
	{
		DELETE(cur_user.next->direct[index].ufd[i].filename, index);
	}
	//把最后一个文件的位置放到删除的位置
	cur_user.next->direct[index].cur_file_size = cur_user.next->direct[cur_user.next->cur_user_direct_size - 1].cur_file_size;  //注意这里	需要减一,由于本身结构的限制
	cur_user.next->direct[index].directname = cur_user.next->direct[cur_user.next->cur_user_direct_size - 1].directname;
	//改变文件的位置
	for (int i = 0; i < cur_user.next->direct[cur_user.next->cur_user_direct_size - 1].cur_file_size; i++)  //注意这里的减一
	{
		cur_user.next->direct[index].ufd[i].addr = cur_user.next->direct[cur_user.next->cur_user_direct_size - 1].ufd[i].addr;
		cur_user.next->direct[index].ufd[i].filename = cur_user.next->direct[cur_user.next->cur_user_direct_size - 1].ufd[i].filename;
		cur_user.next->direct[index].ufd[i].length = cur_user.next->direct[cur_user.next->cur_user_direct_size - 1].ufd[i].length;
		cur_user.next->direct[index].ufd[i].protect_code = cur_user.next->direct[cur_user.next->cur_user_direct_size - 1].ufd[i].protect_code;
	}
	cur_user.next->cur_user_direct_size--; //目录数量减1
	cout << "删除目录成功" << endl;
	return 1;
}

DIR函数(显示当前目录)

  1. 循环找到当前文件夹
  2. 判断是否在根目录
  3. 是则循环输出文件名,否则输出文件相关信息

 

int DIR()//显示当前地址下的内容
{
	int index;
	for (index = 0; index < cur_user.next->cur_user_direct_size; index++)
	{
		if (path == cur_user.next->direct[index].directname)
		{
			break;
		}
	}
	if (path == "")
	{
		cout << "  目录名" << endl;
		for (int i = 0; i < cur_user.next->cur_user_direct_size; i++)
		{
			cout << "  " << cur_user.next->direct[i].directname << endl;
		}
	}
	else
	{
		cout << "\t文件名\t文件保护码\t文件长度\t文件起始盘块号\t\t创建日期" << endl;
		for (int i = 0; i < cur_user.next->direct[index].cur_file_size; i++)
		{
			cout << "\t" << cur_user.next->direct[index].ufd[i].filename << "\t" << cur_user.next->direct[index].ufd[i].protect_code << "\t" << "\t" <<cur_user.next->direct[index].ufd[i].length << "\t" << "\t" << cur_user.next->direct[index].ufd[i].addr << "\t" << "\t" << "\t" <<  cur_user.next->direct[index].ufd[i].time << endl;
		}
	}
	return 1;
}

 

SET函数(设置文件保护码)

  1. 循环找到文件夹
  2. 循环找文件
  3. 判断文件是否已打开(打开不可设置保护码)
  4. 更改保护码

 

int SET(string name,int protectcode)//设置文件保护码
{
	int index;
	for (index = 0; index < cur_user.next->cur_user_direct_size; index++)//找到文件夹的位置
	{
		if (path == cur_user.next->direct[index].directname)
		{
			break;
		}
	}
	int i = 0;
	for (i = 0; i < cur_user.next->direct[index].cur_file_size; i++)//找文件
	{
		if (name == cur_user.next->direct[index].ufd[i].filename)
			break;
	}
	if (i >= cur_user.next->direct[index].cur_file_size)
	{
		cout << "该用户没有这个文件" << endl;
		return 0;
	}
	int j;
	for (j = 0; i < cur_opentable->cur_openfilesize; j++)
	{
		if (cur_opentable->uof[j].filename == path + "/" + name)
		break;
	}
	if (j < cur_opentable->cur_openfilesize)
	{
		cout << "请先关闭该文件" << endl;
		return 0;
	}
	cur_user.next->direct[index].ufd[i].protect_code = protectcode;
	return 1;
}

 

fatused函数(输出块的状态)

类似位示图

 

//输出块的状态
int fatused()
{

	for (int i = 0; i < BLOCK_NUM; i++)//判断是否有空闲块 磁盘块数量64 每个块大小512
		{
			// cout << fat[i].used << " ";
			printf("%d  ",fat[i].used);
			if(i % 8 == 0)
			{
				cout << endl;
			}
		}
		cout << endl;
	return 1;
}

 

问题总结和心得

  1. 熟练运用指针和结构体构建文件整体结构

  2. 块号位示图问题

    • 删除文件后,该文件的块号状态应置为空闲
    • 将最后一块的块号状态置0
    • 最后一个块号的内容替换到删除的位置
    • 如果已经是最后一块,则直接删除并置为空闲
  3. 当前用户问题

    • 当登录后,此用户为当前用户
    • 找到当前用户的文件数组
  4. 文件读写问题

    • 文件被打开时才可进行读写操作
    • 通过文件保护码,判断文件是否可写可读(2:可读可写;0:不可读;其他:可读)
  5. 文件保护问题

    • 当文件被打开时不可删除
    • 当文件被打开时不可修改文件保护码
    • 可读可写保护
    • 追加信息写入问题
    • 每个用户只能访问自己的文件夹
  6. 删除文件问题

    • 先进入对应文件夹
    • 找到要删除的文件
    • 判断文件是否被打开
    • 删除文件信息
    • 清空文件所占用的空间

代码整合

#include <iostream>
#include <string>
#include <malloc.h>
#include<time.h>
using namespace std;

#define BLOCK_SIZE     		512  //每一个磁盘块的大小
#define BLOCK_NUM       	64   //磁盘块数量
#define MAX_FILE_NUM		16   //每个文件夹的最大文件数
#define MAX_FOLDER_NUM  	16	 //每个用户可以拥有的最大文件夹数
#define MAX_OPEN_FILE_NUM   16	 //最大同时打开文件数
#define MAX_USER_NUM		16   //最大用户数

struct MFD//用户数据结构
{
	string username;//用户名
	string password;//密码
	struct USER_UFD *next;//指向用户文件的指针
};

struct UFD//文件夹数据结构 
{
	struct file_message//文件数据结构
	{
		string filename;//文件名
		int protect_code;//文件保护码
		int length;//文件长度
		int addr;//文件起始地址
		string time;	//日期时间,格式为yyyymmdd hhmmss
        int used;//是否被占用 0:空闲 1:占用
	}ufd[MAX_FILE_NUM];//一个文件夹最对可以有16个文件
	string directname;//文件夹名字
	int cur_file_size = 0;//当前该文件夹含有的文件数量
};

struct UOF//打开文件数据结构
{
	struct uof
	{
		string filename;//文件名字
		int pointer;//文件长度
		int protect_code;//文件保护码  默认2  2为可读可写  0为不可读  其余数字不可写
		int addr;//文件起始地址
	}uof[MAX_OPEN_FILE_NUM];//最多可以同时打开16个文
	int cur_openfilesize = 0;//当前打开的文件数量
};

struct fat//块号
{
	int next = -1;//存放下一页的块号,无为-1
	int used = 0;  //used存在三种状态,1:被占用 0:空闲
}fat[BLOCK_NUM];//最大为64

struct USER_UFD//用户文件数据结构
{
	struct UFD direct[MAX_FOLDER_NUM];//一个用户最多可以有16个文件夹
	int cur_user_direct_size = 0;//当前拥有的文件夹数量
};

int mark = 1;
struct USER_UFD cur_user_all_direct_array[MAX_USER_NUM];//用户文件数组 16
struct MFD cur_user;//当前用户
struct UOF * cur_opentable;//指向打开文件数组的指针
char *fdisk;//模拟磁盘指针
struct UOF openfile[MAX_OPEN_FILE_NUM];//每一个用户对应一个文件打开表对象,16
struct MFD mfd[MAX_USER_NUM]; //16个用户
int cur_user_size = 0;//记录当前用户的人数
string path;

int REGISTER();//注册用户
int LOGIN();//登录用户
int CREATE(string name);//创建文件
int DELETE(string name);//删除文件
int OPEN(string name);//打开文件
int WRITE(string name);//写文件
int READ(string name);//读文件
int CLOSE(string name);//关闭文件
int CD();//更改当前目录
int MKDIR(string name);//创建文件夹
int DIR();//显示当前目录的子目录
int SET(string name, int protectcode);//设置保护码
void INPUT_OPERATION();//指令输入

int REGISTER()//注册用户
{
	cout << "请输入用户名:";
	string username;
	cin >> username;
	cout << "请输入密码:";
	string password;
	cin >> password;
	for (int i = 0; i < MAX_USER_NUM; i++)//最大可有16个用户
	{
		if (mfd[i].username == username)
		{
			cout << "注册失败,该用户名已存在" << endl;
			return 0;
		}
	}
	//将用户信息存入用户结构体中
	mfd[cur_user_size].username = username;
	mfd[cur_user_size].password = password;
	cur_user_size++;
	cout << "注册成功" << endl;
	return 1;
}

int LOGIN()//用户登入
{
	cout << "请输入用户名:";
	string username;
	cin >> username;
	cout << "请输入密码:";
	string password;
	cin >> password;
	int i;
	for (i = 0; i < cur_user_size; i++)
	{
		if (mfd[i].username == username)
		{
			if (mfd[i].password != password)
			{
				cout << "密码错误" << endl;
				return 0;
			}
			else
			{
				break;
			}
		}
	}
	if (i == cur_user_size)
	{
		cout << "没有此用户" << endl;
		return 0;
	}
	mfd[i].next = &(cur_user_all_direct_array[i]);//用户文件指向第i个用户数组
	cur_user = mfd[i];//登入后此为当前目录
	cur_user.next->cur_user_direct_size = mfd[i].next->cur_user_direct_size;//让当前用户找到所登入的用户文件
	cur_user_size++;//用户人数+1
	cur_opentable = &openfile[cur_user_size]; //指针指向文件打开表对象  每一个用户一个文件打开表对象,该表中最多存放16个文件夹
	cur_opentable->cur_openfilesize = 0;//设置当前打开的文件数为0
	path = "";//设置path在刚登入时为空
	return 1;
}

//查看用户表
int LOOK()
{
	int i;
	for (i = 0; i < cur_user_size; i++)
	{
		cout << mfd[i].username << "  ";//遍历现有的用户数,方便观察
	}
	printf("\n"); 
	return 1;
}

string getCurrentTimeStr()//得到时间
{
	time_t t = time(NULL);
	char ch[64] = {0};
	strftime(ch, sizeof(ch) - 1, "%Y-%m-%d %H:%M:%S", localtime(&t));
	return ch;
}

//创建文件
int CREATE(string name)
{
	int index;
	if (path == "")
	{
		cout << "请先进入文件夹,再创建文件" << endl;
		return 0;
	}
	for (index = 0; index < cur_user.next->cur_user_direct_size; index++)
	{
		if (path == cur_user.next->direct[index].directname)//如果path等于文件夹名
		{
			break;
		}
	}
	int i;
	for (i = 0; i < cur_user.next->direct[index].cur_file_size; i++)//循环当前文件夹的文件数
	{
		if (name == cur_user.next->direct[index].ufd[i].filename)
		{
		break;//文件名重复跳出循环
		}
	}
	if (i < cur_user.next->direct[index].cur_file_size)//重复 i会小于文件数,无重复 则等于
	{
		cout << "文件名重复" << endl;
		return 0;
	}
	if (cur_user.next->direct[index].cur_file_size == MAX_OPEN_FILE_NUM)
	{
		cout << "该文件夹文件已达到"<< MAX_OPEN_FILE_NUM << "个" << endl;
		return 0;
	}
	int j;
	for (j = 0; j < BLOCK_NUM; j++)//判断是否有空闲块 磁盘块数量64 每个块大小512
	{
		if (fat[j].used == 0)
		{
			break;
		}
			
	}
	if (j >= BLOCK_NUM)
	{
		cout << "磁盘没有空闲块了" << endl;
		return 0;
	}

	string tim=getCurrentTimeStr();
	cout << tim << endl;
	

	cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size].time = tim;//创建时间
	cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size].filename = name;//上面都通过了保存文件名
	cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size].addr = j; //文件起始盘块号
	cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size].length = 0;//未写入数据,长度为0
	cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size].protect_code = 2; //表示可读可写
	//cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size].time = tim;//创建时间
	cur_user.next->direct[index].cur_file_size++;//用户文件数量加1
    fat[j].used = 1;//此时已被占用
	return 1;
}

//删除文件
int DELETE(string name, int mark)
{
	if (path == "" && mark == -1)
	{
		cout << "请在文件夹中删除文件" << endl;
		return 0;
	}
	int index;
	if(mark != -1) 	//如果是被RMDIR函数调用,则当前path=""
		index = mark;
	else
		for (index = 0; index < cur_user.next->cur_user_direct_size; index++)//循环用户拥有的文件夹
		{
			if (path == cur_user.next->direct[index].directname)//找到文件夹后退出
			{
				break;
			}
		}

	int i;
	for (i = 0; i < cur_user.next->direct[index].cur_file_size; i++)//循环该文件夹的文件
	{
		if (cur_user.next->direct[index].ufd[i].filename == name)//找到文件后退出
		{
			break;
		}
	}
	if (i >= cur_user.next->direct[index].cur_file_size)
	{
		cout << "没有该文件" << endl;
		return 0;
	}
	int j;
	for (j = 0; i < cur_opentable->cur_openfilesize; j++)//循环打开的文件,查看是否被打开
	{
		if (cur_opentable->uof[j].filename == name)
			break;
	}
	if (j < cur_opentable->cur_openfilesize)
	{
		cout << "该文件已被打开,无法删除" << endl;
		return 0;
	}

	//更新当前用户目录下文件数组信息,就是将最后一个文件的信息替换到要删除的文件的位置

	//如果本身是最后一个文件,那么不需要替换,直接删除
	if(i == cur_user.next->direct[index].cur_file_size -1)
	{
		fat[cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size - 1].addr].used = 0; 
		cur_user.next->direct[index].cur_file_size--;
		//next为-1 说明没有下一页,此为最后一页
		int temp = fat[cur_user.next->direct[index].ufd[i].addr].next;
		while (temp != -1)//删除该文件的内存空间
		{
			fat[temp].used = 0;
			temp = fat[temp].next;
		}
		cout << "删除文件" << name << "成功" << endl;
		return 1;
	}
	else
	{
		cur_user.next->direct[index].ufd[i].filename = cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size - 1].filename;
		//先将该块置0
		fat[cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size - 1].addr].used = 0; 
		cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size - 1].addr = cur_user.next->direct[index].ufd[i].addr;
		cur_user.next->direct[index].ufd[i].length = cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size - 1].length;
		cur_user.next->direct[index].ufd[i].protect_code = cur_user.next->direct[index].ufd[cur_user.next->direct[index].cur_file_size - 1].protect_code;
		cur_user.next->direct[index].cur_file_size--;

		//将该文件设置为占用
		fat[cur_user.next->direct[index].ufd[i].addr].used = 1;
		
		//next为-1 说明没有下一页,此为最后一页
		int temp = fat[cur_user.next->direct[index].ufd[i].addr].next;
		while (temp != -1)//删除该文件的内存空间
		{
			fat[temp].used = 0;
			temp = fat[temp].next;
		}
		cout << "删除文件" << name << "成功" << endl;
		return 1;
	}
}

int OPEN(string name)//打开文件
{
    if (path == "")
	{
		cout << "请在文件夹中打开文件" << endl;
		return 0;
	}
	string AllName = path +'/'+name;//设置该变量的原因是防止出现打开文件数组中存在文件夹名不同而文件同名的情况
	int index;
	for (index = 0; index < cur_user.next->cur_user_direct_size; index++)//循环当前用户的文件
	{
		if (path == cur_user.next->direct[index].directname)
		{
			 break;
		}
	}
	int i = 0;
	for (i = 0; i < cur_user.next->direct[index].cur_file_size; i++)//循环文件数
	{
		if (name == cur_user.next->direct[index].ufd[i].filename)//找到同名文件
			 break;
	}
	if (i >= cur_user.next->direct[index].cur_file_size)
	{
		cout << "没有该文件" << endl;
		return 0;
	}
	if (cur_opentable->cur_openfilesize == MAX_OPEN_FILE_NUM)
	{
		cout << "文件打开数量已经达到最大值" << endl;
		return 0;
	}
	
	for (int j = 0; i < cur_opentable->cur_openfilesize; j++)
	{
		if (cur_opentable->uof[i].filename == AllName)//在打开文件中找到相应文件夹中的文件
		{
			 cout << "该文件已经打开" << endl;
			 return 0;
		}
	}
	int k;
	for (k = 0; k < cur_user.next->direct[index].cur_file_size; k++)
	{
		if (cur_user.next->direct[index].ufd[k].filename == name)//找到相应文件夹中的文件
		 break;
	} 
	//将该文件的信息加入到已打开的文件数组中
	cur_opentable->uof[cur_opentable->cur_openfilesize].filename = AllName;//在打开的文件数组中加入该文件
	cur_opentable->uof[cur_opentable->cur_openfilesize].protect_code = 	cur_user.next->direct[index].ufd[k].protect_code;//保护码
	cur_opentable->uof[cur_opentable->cur_openfilesize].pointer = cur_user.next->direct[index].ufd[k].length;
	cur_opentable->uof[cur_opentable->cur_openfilesize].addr = cur_user.next->direct[index].ufd[k].addr;//起始地址
	cur_opentable->cur_openfilesize++; //文件打开数量加1
	cout << "文件打开成功" <<endl;
	return k; //返回文件在文件打开表中的第几项
}

int CLOSE(string name)//关闭文件
{	
	int i;
	for (i = 0; i < cur_opentable->cur_openfilesize; i++)
	{
		if (cur_opentable->uof[i].filename == path + '/' + name)
		{
			break;
		}
	}
	if (i >= cur_opentable->cur_openfilesize)
	{
		cout << "该文将未打开" << endl;
		return 0;
	}
	//更新当前用户打开文件数组信息,就是将最后一个打开文件的信息替换到要关闭的打开文件的位置
	cur_opentable->uof[i].filename = cur_opentable->uof[cur_opentable->cur_openfilesize - 1].filename;
	cur_opentable->uof[i].pointer = cur_opentable->uof[cur_opentable->cur_openfilesize - 1].pointer;
	cur_opentable->uof[i].protect_code = cur_opentable->uof[cur_opentable->cur_openfilesize - 1].protect_code;
	cur_opentable->uof[i].addr = cur_opentable->uof[cur_opentable->cur_openfilesize - 1].addr;
	cur_opentable->cur_openfilesize--;
	cout << "文件关闭成功" << endl;
	return 1;
}

int WRITE(string name)//写入文件
{
	int index; //标识当前目录在direct数组中第几个
	for (index = 0; index < cur_user.next->cur_user_direct_size; index++)
	{
		if (path == cur_user.next->direct[index].directname)//在当前用户的文件夹中找到相应的文件
		{
			break;
		}
	}
	int i;
	//判读文件是否打开
	for (i = 0; i < cur_opentable->cur_openfilesize; i++)
	{
		if (cur_opentable->uof[i].filename == path + '/' + name)//循环打开的文件
			break;
	}
	if (i >= cur_opentable->cur_openfilesize)
	{
		cout << "文件没有打开, 无法写入" << endl;
		return -1;
	}
	int fd = i; //获取文件描述字
	//判断读文件的合法性
	if (cur_opentable->uof[fd].protect_code != 2)
	{
		cout << "文件不可写" << endl;
		return -1;
	}
	else
	{
		string content;//字符串  存储输入的一行内容
		cin.ignore();
		//cin.ignore()函数是C++标准输入流(cin)中的一个方法。cin.ignore()函数中有两个参数,
		//分别为数值型的a 和 字符型的 ch ,即cin.ignore( a, ch )。它表示从输入流 cin 中提取
		//字符,提取的字符被忽略,不被使用。而每抛弃一个字符,它都要进行计数和比较字符:如果
		//计数值达到 a 或者被抛弃的字符是 ch ,则cin.ignore() 函数执行终止;否则,它继续等待。
		//如果默认不给参数的话,默认参数为cin.ignore(1, EOF),即把EOF前的1个字符清掉,没有遇
		//到EOF就清掉一个字符然后结束。
		cout << "请输入文件要写入的内容: " << endl;;
		getline(cin, content); //读入一整行内容
		int cin_len = content.length();//输入一行的长度
		int cin_p=0;//初值为0
		int last_block_res;//磁盘剩余空间的大小

		//追加写
		//找到该文件存放的最后一个磁盘块  
		int last_block = cur_opentable->uof[fd].addr;
		while (fat[last_block].next != -1)
		{
			last_block = fat[last_block].next;
		}

		int temp;//保存当前所写的文件在用户文件目录表的第几项,为了后面修改文件的大小
		for (int k = 0; k < cur_user.next->direct[index].cur_file_size; k++)
		{
			if (cur_user.next->direct[index].ufd[k].filename == name)//找到该文件
			{
				temp = k;
				break;
			}
		}

		//计算该文件存放的最后一个地址
		char  * first;
		first = fdisk + last_block * BLOCK_SIZE + cur_opentable->uof[fd].pointer % BLOCK_SIZE;

		//计算最后一个磁盘块还有多少空间
		if(cur_opentable->uof[fd].pointer == 0)
			last_block_res = BLOCK_SIZE;
		else if(cur_opentable->uof[fd].pointer % BLOCK_SIZE == 0)
			last_block_res = 0;
		else
			last_block_res = BLOCK_SIZE - (cur_opentable->uof[fd].pointer % BLOCK_SIZE);

		//首先填满最后一个磁盘块
		if(last_block_res)
		{
			if(last_block_res <= cin_len)	//当最后一个磁盘块的空间小于输入内容大小
			{
				for(int j=0;j<last_block_res;j++)
					first[j] = content[cin_p++];
				cin_len -= last_block_res;//计算还有多少字符没存入
			}
			else	//当最后一个磁盘块的空间大于输入内容大小
			{
				for(int j=0;j<cin_len;j++)
					first[j] = content[cin_p++];
				cur_opentable->uof[fd].pointer = cur_opentable->uof[fd	].pointer + cin_len;  //更新文件打开表
				cur_user.next->direct[index].ufd[temp].length = cur_user.next->direct[index].ufd[temp].length + cin_len; //更新用户目录文件表
				cout << "文件写入成功,共写入" << cin_len << "字节" << endl;
				return 0;
			}
		}

		//计算还需要多少个磁盘块,判断空间是否足够
		int times = cin_len / BLOCK_SIZE;//向上取整 求存满块数
		int offset = cin_len % BLOCK_SIZE;//取余  未存满
		if (offset != 0)
			times++;//块数+1

		int unused_block_num = 0; //记录没有使用过的磁盘块的个数
		int flag = 0;
		for (int j = 0; j < BLOCK_NUM; j++)
		{
			if (fat[j].used == 0)
			{
				unused_block_num++;
			}
		}

		if(unused_block_num < times)//剩余的块不够写
		{
			cout<<"空间不足,写入内容失败!\n";
			return -1;
		}

		int next_block = fat[cur_opentable->uof[fd].addr].next; //记录块的起始地址的next
		int first_unused_block; //记录第一个没有被使用过的磁盘
		for(int j=0;j<times;j++)
		{
			for (int k = 0; k < BLOCK_NUM; k++)
			{
				if (fat[k].used == 0)
				{
					first_unused_block = k;
					break;
				}
			}
			fat[last_block].next = first_unused_block;
			last_block = first_unused_block;
			first = fdisk + last_block * BLOCK_SIZE;
			if(j!=times-1)
			{
				for(int k = 0;k<BLOCK_SIZE;k++)
					first[k] = content[cin_p++];
			}
			else
			{
				for(int k=0;k<offset;k++)
					first[k] = content[cin_p++];
			}

		}
		cur_opentable->uof[fd].pointer = cur_opentable->uof[fd].pointer + cin_p;  //更新文件打开表
		cur_user.next->direct[index].ufd[temp].length = cur_user.next->direct[index].ufd[temp].length + cin_p; //更新用户目录文件表
		cout << "文件写入成功,共写入" << cin_p << "字节" << endl;
		return 0;
	}
}

int READ(string name)//读文件
{
	int index1; //标识当前目录在direct数组中第几个
	for (index1 = 0; index1 < cur_user.next->cur_user_direct_size; index1++)
	{
		if (path == cur_user.next->direct[index1].directname)//找到文件夹
		{
			break;
		}
	}
	int a;
	for (a = 0; a < cur_user.next->direct[index1].cur_file_size; a++)    //判断文件是否存在
	{
	if (cur_user.next->direct[index1].ufd[a].filename == name)//找文件
		break;
	}
	if (a >= cur_user.next->direct[index1].cur_file_size)
	{
		cout << "没有这个文件" << endl;
		return 0;
	}
	int i;
	//判读文件是否打开
	for (i = 0; i < cur_opentable->cur_openfilesize; i++)
	{
		if (cur_opentable->uof[i].filename == path + '/' + name)
			break;
	}
	if (i >= cur_opentable->cur_openfilesize)
	{
		cout << "文件没有打开, 无法读取" << endl;
		return -1;
	}
	int fd = i; //获取文件描述字
	//判断读文件的合法性
	if (cur_opentable->uof[fd].protect_code == 0) //我们创建的文件都是默认可读可写的
	{
		cout << "文件不可读" << endl;
		return 0;
	}
	else
	{
		int len = cur_opentable->uof[fd].pointer; //文件的长度
		int block_num = len / BLOCK_SIZE; //磁盘的个数
		int offset = len % BLOCK_SIZE; //偏移量
		if (offset != 0)
			block_num++;
		
		//如果我用一个文件表示磁盘的引导块,用另一个文件表示磁盘的	数据块,那么我们计算文件的起始位置就不用加上磁盘的引导块了吧
		//关于文件的存放文件,我们char *fdisk表示一整个磁盘,然后不		同文件的内容存放在这个指针所指向的不同字符段
		char * first = fdisk + cur_opentable->uof[fd].addr * BLOCK_SIZE; //文件的起始地址 第0块起始地址为0 第1块起始地址为512
		char * buf = (char *)malloc(513 * sizeof(char)); //缓冲区  给指针申请513个char类型大小的空间  512个空间存放内容 第513个存\0
		cout << "文件的内容为 :" << endl;
		for (int k = 0; k < block_num; k++)
		{
			if (k == block_num - 1)  //则是最后一个磁盘块
			{
				int j;
				for (j = 0; j < len - k * BLOCK_SIZE; j++)  //赋值文件剩余的字符,其实就是偏移量
				{
					buf[j] = first[j];
				}
				buf[j] = '\0';//\0结束
				printf("%s\n", buf); //输出文件的内容
			}
			else //不在最后一个磁盘块,也就是在其他已经读满的磁盘块
			{
				int j;
				for (j = 0; j < BLOCK_SIZE; j++)
					buf[j] = first[j]; //缓冲区读满就输出内容
				buf[j] = '\0';
				printf("%s", buf); //输出文件的内容
				int next_block = fat[cur_opentable->uof[fd].addr].next; 				//读完一个磁盘块后,在接着读下一个磁盘块
				first = fdisk + next_block * BLOCK_SIZE;
			}
		}
		cout << endl;
		free(buf); //释放缓冲区
		return 0;
	}
}

int CD()//更改当前目录
{
	string temp_path;
	cin >> temp_path;
	if (temp_path == "..")
	{
		if(path == "")
		{
			cout << "已在根目录" << endl;
		}
		else
		{
			path = "";
		  	
		}
			return 1;
	}
	int i;
	for (i = 0; i < cur_user.next->cur_user_direct_size; i++)
	{
		if (temp_path == cur_user.next->direct[i].directname)
			break;
	}
	if (i >= cur_user.next->cur_user_direct_size)
	{
		cout << "没有此目录" << endl;
		return 0;
	}
	path = temp_path;//path为该路径
	return 1;
}

int MKDIR(string name)//创建目录
{
	if (cur_user.next->cur_user_direct_size == MAX_USER_NUM)
	{
		cout << "用户目录数量已经达到最大值" << endl;
		return 0;
	}
	int i;
	for (i = 0; i < cur_user.next->cur_user_direct_size; i++)
	{
		if (cur_user.next->direct[i].directname == name)
			break;
	}
	if (i < cur_user.next->cur_user_direct_size)
	{
		cout << "该目录名已存在" << endl;
		return 0;
	}
	cur_user.next->direct[cur_user.next->cur_user_direct_size].directname = name;
	cur_user.next->direct[cur_user.next->cur_user_direct_size].cur_file_size = 0; //新创建的目录里面的文件个数为0
	cur_user.next->cur_user_direct_size++;
	cout << "创建目录成功" << endl;
	return 1;
}

int RMDIR(string name)//删除目录
{
	if(path != "")
	{
		cout << "请先退出文件夹" << endl;
		return 0;
	}
	int index;//文件夹的位置
	for (index = 0; index < cur_user.next->cur_user_direct_size; index++)//先判断是否有该文件夹
	{
		if (name == cur_user.next->direct[index].directname)
			break;
	}

	for (int i = 0; i < cur_user.next->direct[index].cur_file_size; i++)
	{
		for(int j=0;i<cur_opentable->cur_openfilesize;j++)
			if (name + '/' + cur_user.next->direct[index].ufd[i].filename 	== cur_opentable->uof[j].filename)
			{
				cout << "请先关闭该文件夹的所有文件" << endl;
				return 0;
			}
	}
	//删除该文件夹的所有文件

	for (int i = 0; i < cur_user.next->direct[index].cur_file_size; i++)
	{
		DELETE(cur_user.next->direct[index].ufd[i].filename, index);
	}
	//把最后一个文件的位置放到删除的位置
	cur_user.next->direct[index].cur_file_size = cur_user.next->direct[cur_user.next->cur_user_direct_size - 1].cur_file_size;  //注意这里	需要减一,由于本身结构的限制
	cur_user.next->direct[index].directname = cur_user.next->direct[cur_user.next->cur_user_direct_size - 1].directname;
	//改变文件的位置
	for (int i = 0; i < cur_user.next->direct[cur_user.next->cur_user_direct_size - 1].cur_file_size; i++)  //注意这里的减一
	{
		cur_user.next->direct[index].ufd[i].addr = cur_user.next->direct[cur_user.next->cur_user_direct_size - 1].ufd[i].addr;
		cur_user.next->direct[index].ufd[i].filename = cur_user.next->direct[cur_user.next->cur_user_direct_size - 1].ufd[i].filename;
		cur_user.next->direct[index].ufd[i].length = cur_user.next->direct[cur_user.next->cur_user_direct_size - 1].ufd[i].length;
		cur_user.next->direct[index].ufd[i].protect_code = cur_user.next->direct[cur_user.next->cur_user_direct_size - 1].ufd[i].protect_code;
	}
	cur_user.next->cur_user_direct_size--; //目录数量减1
	cout << "删除目录成功" << endl;
	return 1;
}

int DIR()//显示当前地址下的内容
{
	int index;
	for (index = 0; index < cur_user.next->cur_user_direct_size; index++)
	{
		if (path == cur_user.next->direct[index].directname)
		{
			break;
		}
	}
	if (path == "")
	{
		cout << "  目录名" << endl;
		for (int i = 0; i < cur_user.next->cur_user_direct_size; i++)
		{
			cout << "  " << cur_user.next->direct[i].directname << endl;
		}
	}
	else
	{
		cout << "\t文件名\t文件保护码\t文件长度\t文件起始盘块号\t\t创建日期" << endl;
		for (int i = 0; i < cur_user.next->direct[index].cur_file_size; i++)
		{
			cout << "\t" << cur_user.next->direct[index].ufd[i].filename << "\t" << cur_user.next->direct[index].ufd[i].protect_code << "\t" << "\t" <<cur_user.next->direct[index].ufd[i].length << "\t" << "\t" << cur_user.next->direct[index].ufd[i].addr << "\t" << "\t" << "\t" <<  cur_user.next->direct[index].ufd[i].time << endl;
		}
	}
	return 1;
}

int SET(string name,int protectcode)//设置文件保护码
{
	int index;
	for (index = 0; index < cur_user.next->cur_user_direct_size; index++)//找到文件夹的位置
	{
		if (path == cur_user.next->direct[index].directname)
		{
			break;
		}
	}
	int i = 0;
	for (i = 0; i < cur_user.next->direct[index].cur_file_size; i++)//找文件
	{
		if (name == cur_user.next->direct[index].ufd[i].filename)
			break;
	}
	if (i >= cur_user.next->direct[index].cur_file_size)
	{
		cout << "该用户没有这个文件" << endl;
		return 0;
	}
	int j;
	for (j = 0; i < cur_opentable->cur_openfilesize; j++)
	{
		if (cur_opentable->uof[j].filename == path + "/" + name)
		break;
	}
	if (j < cur_opentable->cur_openfilesize)
	{
		cout << "请先关闭该文件" << endl;
		return 0;
	}
	cur_user.next->direct[index].ufd[i].protect_code = protectcode;
	return 1;
}

//输出块的状态
int fatused()
{

	for (int i = 0; i < BLOCK_NUM; i++)//判断是否有空闲块 磁盘块数量64 每个块大小512
		{
			// cout << fat[i].used << " ";
			printf("%d  ",fat[i].used);
			if(i % 8 == 0)
			{
				cout << endl;
			}
		}
		cout << endl;
	return 1;
}


void INPUT_OPERATION()//指令输入
{
	if (cur_user.username == "")
		cout << "localhost :";
	else
		cout << cur_user.username << "@localhost  home/" << path << ":";
	string operaton;
	cin >> operaton;
	if (operaton == "login")
	{
		LOGIN();
	}
	else if (operaton == "create")
	{
		string filename;
		cin >> filename;
		CREATE(filename);
	}
	else if (operaton == "del")
	{
		string name;
		cin >> name;
		DELETE(name, -1);
	}
	else if (operaton == "open")
	{
		string name;
		cin >> name;
		OPEN(name);
	}
	else if (operaton == "close")
	{
		string name;
		cin >> name;
		CLOSE(name);
	}
	else if (operaton == "write")
	{
		string content;
		string name;
		cin >> name;
		WRITE(name);
	}
	else if (operaton == "read")
	{
		string name;
		cin >> name;
		READ(name);
	}
	else if (operaton == "exit")
	{
		exit(0);
	}
	else if (operaton == "cd")
	{
	CD();
	}
	else if (operaton == "dir")
	{
		DIR();
	}
	else if (operaton == "mkdir")
	{
		string name;
		cin >> name;
		MKDIR(name);
	}
	else if (operaton == "register")
	{
		REGISTER();
	}
	else if (operaton == "exit")
	{
		mark = 0;
	}
	else if (operaton == "set")
	{
		string name;
		int protextcode;
		cin >> name;
		cin >> protextcode;
		SET(name, protextcode);
	}
	else if (operaton == "rmdir")
	{
		string name;
		cin >> name;
		RMDIR(name);
	}
	else if (operaton == "look")
	{
		LOOK();
	}
	else if (operaton == "fatused")
	{
		fatused();
	}
	else
	{
		cout << "命令错误,请重新输入" << endl;
	}
}

int main()
{
	cur_user.username = "";
	path = "";
	fdisk = (char *)malloc(1024 * 1024 * sizeof(int));//模拟硬盘指针
	
	//注册root用户并登录,方便调试代码
	mfd[cur_user_size].username = "root";
	mfd[cur_user_size].password = "123456";
	cur_user_size++;
	mfd[0].next = &(cur_user_all_direct_array[0]);
	cur_user = mfd[0];
	cur_user.next->cur_user_direct_size = mfd[0].next->cur_user_direct_size;
	cur_user_size++;
	cur_opentable = &openfile[cur_user_size]; //指针指向文件打开表对象
	cur_opentable->cur_openfilesize = 0;
	
	cout << "*******************欢迎使用二级文件系统*******************" << endl;
	cout << "        命令格式                说明                      " << endl;
	cout << "        register                注册用户                  " << endl;
	cout << "        login                   登录                      " << endl;
	cout << "        cd 目录名               更改当前目录              " << endl;
	cout << "        mkdir 目录名            创建子目录                " << endl;
	cout << "        rmdir 目录名            删除子目录                " << endl;
	cout << "        dir                     显示当前目录的子目录      " << endl;
	cout << "        create 文件名           创建文件                  " << endl;
	cout << "        del 文件名              删除文件                  " << endl;
	cout << "        open 文件名             打开文件                  " << endl;		
	cout << "        close                   关闭文件                  " << endl;
	cout << "        read 文件名               读文件                    " << endl;
	cout << "        write 文件名              写文件                    " << endl;
	cout << "        set 文件名 文件保护码   设置文件保护码            " << endl;
	cout << "        exit                    退出系统                  " << endl;
	cout << "        look                    查看用户                 " << endl;
	cout << "        fatused                 查看块                 " << endl;
	while (mark)
		INPUT_OPERATION();
	free(fdisk);
	return 1;
}

此文章为学习记录^_^ 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值