山东大学数据结构与算法课程设计实验3模拟文件目录系统

问题描述

设计并实现目录树 CatalogTree 的ADT,用它来表达字符串集合组成的有序树。应用以上CatalogTree结构设计并实现一文件目录系统的模拟程序,并提供模拟操作界面。

针对于目录系统,CatalogTree 的结点存放的数据内容为字符串,每个结点对应一个目录项,该目录项可以是目录,也可以是文件,如果是目录就可以再存放其它目录或文件,即非叶结点;如果是文件就是叶结点。从根结点到该结点路径所有结点的字符串用“/”进行组合后就是该目录项的绝对路径,用来唯一的标识该目录。例如:

/usr/li/email/student/。

目录系统具有如下基本操作:

① dir ——列出当前目录下的所有目录项

② cd ——打出当前目录的绝对路经

③ cd ..——当前目录变为当前目录的父目录

④ cd str——当前目录变为 str 所表示路径的目录

⑤ mkdir str ——在(当前目录下)创建一个子目录(名为 str)

⑥ mkfile str ——在(当前目录下)创建一个文件(名为 str)

⑦ delete str ——删除(当前目录下)名为 str 的目录或文件

基本要求

  (1)描述并实现CatalogTree的ADT,包括其上的基本操作:如插入一个结点,寻找一个结点,返回一个结点的最左儿子等(具体情况依据应用自定)。

(2)应用CatalogTree的ADT实现一个模拟文件目录系统的应用程序。

(3)应用程序是一个不断等待用户输入命令的解释程序,根据用户输入的命令完成相关操作,直到退出(quit)。命令名及其含义如上所述。

(4)目录树结构可以保存(save)到文件中,也可从文件中读出(load *.dat)。

(5)dir命令的结果应能够区分是子目录和还是文件。

(6)应对命令 ④~⑦中的 str 区分是绝对路径,还是相对路径。

数据结构与算法描述

 需要构建一个节点结构体,包括此目录项的名字,父目录,以及当前目录的子目录数组,当前目录的文件数组。

struct Node {
	string name;
	vector<Node*> children;             //当前目录的子目录
	vector<string> files;            //当前目录的文件
	Node* father;
	Node(string s, Node* p1 = NULL) {
		name = s;
		father = p1;
	}
};

构造一个目录树。其包括题目要求中的所有操作函数。私有成员包括输出的文件、根目录、现在访问的目录的位置。

Dir函数即通过两个for循环遍历当前目录节点的子目录数组和文件数组两个数组,并输出,同时用两个*区分文件和目录。

void dir() {//列出当前目录下的所有目录项(子目录和文件)
		for (const auto& file : now->files) outfile << '*' << file << '*' << endl;// 输出当前目录的文件
		for (const auto& subdir : now->children) outfile << subdir->name << endl;// 输出当前目录的子目录
	}

Cdap函数,即打出当前目录的绝对路经,如果当前目录为根目录输出一个‘/’,然后用一个空字符串,从当前目录依次向父目录遍历直到根目录,同时更新此字符串,在此字符串前加父目录名,最后输出。

void CatalogTree::cdap() {//打出当前目录的绝对路经
	if (now == root) { // 如果当前节点为根节点,则输出根目录符号并返回
		outfile << '/' << endl;
		return;
	}
	string absolutePath = ""; // 存储绝对路径的字符串
	Node* currentNode = now;
	while (currentNode != root) {// 遍历直到根节点,将每个节点的名称添加到路径字符串中
		absolutePath = "/" + currentNode->name + absolutePath;
		currentNode = currentNode->father;
	}
	outfile << absolutePath << endl;// 输出绝对路径
}

Cdstr函数,当前目录变为 str 所表示路径的目录,并能区分str是绝对路径还是相对路径。先判断str中是否有‘/’,如果没有则是相对路径,则直接找当前目录的子目录中是否存在名为str的目录,找到并将now当前目录换成名为str的目录。如果是绝对路径,则从根节点开始,依次取出str中的目录名,找对应的子目录,找不到则return,找到最后的子目录将now改为最后的子目录所在节点。

void CatalogTree::cdstr(string path) {
	if (path.find('/') == string::npos) {//当前目录变为s所表示路径的目录,s为该目录下的一个子目录,    
		for (auto subdir : now->children) {// 遍历当前目录下的所有子目录
			if (subdir->name == path) { // 如果找到目标子目录
				now = subdir; // 将当前目录更改为目标子目录
				return; // 结束循环
			}
		}
		// 如果未找到目标子目录,则保持当前目录不变
	}
	else {//path为从根节点开始的绝对路径,找到最终的目标目录项,将其改为当前目录
		path = path.substr(1);// 去除路径中的第一个字符 '/',因为根节点不需要匹配
		Node* currentNode = root;
		while (path.find('/') != string::npos) {// 循环直到路径中不再包含 '/'
			string nextDir = path.substr(0, path.find('/'));//得到该目录下的下一个子目录的名称
			path = path.substr(path.find('/') + 1);// 更新路径,去除已处理的目录名
			// 在当前目录的子目录中查找目标目录
			bool found = false;
			for (auto& subdir : currentNode->children) {
				if (subdir->name == nextDir) {
					currentNode = subdir;
					found = true;
					break;
				}
			}
			// 如果未找到目标目录,则返回当前节点
			if (!found) {
				now = currentNode;
				return;
			}
		}
		for (auto subdir : currentNode->children) {// 在最后一个子目录中查找目标目录
			if (subdir->name == path) {
				now = subdir;
				return;
			}
		}
		now = currentNode;// 如果未找到目标目录,则返回当前节点
	}
}

Cdfather函数如果now当前目录不是根目录的话,直接将now目录更新为其父目录。

void cdfather() {//当前目录变为当前目录的父目录
		if (now != root) { // 如果当前目录不是根目录,则变更当前目录为其父目录
			now = now->father;
		}
		// 如果当前目录是根目录,则不做任何变更
	}

Mkdir函数,先判断str中是否有‘/’,如果没有则是相对路径直接在当前目录的子目录数组中加一个节点(前提是没有相同名字的子目录存在),并进行字典序排序。如果是绝对路径则从根节点开始,依次取出str中的目录名,找是否有相同名字的子目录,如果没有则对当前目录(临时变量且从根目录开始)建立新子目录,然后将当前目录(临时变量)变为此子目录,一直到str的最后一个目录。

void CatalogTree::mkdir(string s){//在(当前目录下)创建一个子目录(名为 s)
	if (s.find('/') == string::npos) {
		for (auto& child_node : now->children) {// 检查当前节点的子节点是否已存在同名节点
			if (child_node->name == s) return;// 如果找到同名节点,则说明已存在,无需再创建
		}
		Node* new_node = new Node(s, now);
		now->children.push_back(new_node);
		if (!now->children.empty()) sort(now->children.begin(), now->children.end(), judge);//对当前节点的子节点按字典序进行排序
	}
	else {
		s = s.substr(1);// 去除路径中的第一个字符 '/',因为根节点不需要匹配
		Node* currentNode = now;
		while (s.find('/') != string::npos) {// 循环直到路径中不再包含 '/'
			string nextDir = s.substr(0, s.find('/'));//得到该目录下的下一个子目录的名称
			 s = s.substr(s.find('/') + 1);// 更新路径,去除已处理的目录名
			for (auto& subdir : currentNode->children) {// 在当前目录的子目录中查找是否已存在同名节点
				if (subdir->name == nextDir) return;
			}
			Node* new_node = new Node(nextDir, currentNode);
			currentNode->children.push_back(new_node);
			if (!currentNode->children.empty()) sort(currentNode->children.begin(), currentNode->children.end(), judge);//对当前节点的子节点按字典序进行排序
			currentNode = new_node;
		}
		for (auto subdir : currentNode->children) {// 在最后一个子目录中查找目标目录
			if (subdir->name == s) return;
		}
		Node* new_node = new Node(s, currentNode);
		currentNode->children.push_back(new_node);
		if (!currentNode->children.empty()) sort(currentNode->children.begin(), currentNode->children.end(), judge);//对当前节点的子节点按字典序进行排序
	}
}

Mkfile函数其功能为创建一个文件,先判断str中是否有‘/’,如果没有则是相对路径直接在当前目录终点files文件数组中加一个名为str的文件(前提是没有相同名字的文件的存在)。如果是绝对路径,则从根节点开始,依次取出str中的目录名,找对应的子目录,找不到则return,找到最后的子目录,找子目录中是否有对应名字的文件,如果没有则对此目录的files数组增加此文件。

void CatalogTree::mkfile(string s){//在(当前目录下)创建一个文件(名为 s) 
	if (s.find('/') == string::npos) {
		for (const auto& filename : now->files) {// 如果找到同名文件,则说明已存在,无需再创建
			if (filename == s) return; // 直接返回,不进行任何操作
		}
		now->files.push_back(s);
		sort(now->files.begin(), now->files.end());//对当前目录下的文件列表按照字典序进行排序
	}
	else {
		s = s.substr(1);// 去除路径中的第一个字符 '/',因为根节点不需要匹配
		Node* currentNode = root;
		while (s.find('/') != string::npos) {// 循环直到路径中不再包含 '/'
			string nextDir = s.substr(0, s.find('/'));//得到该目录下的下一个子目录的名称
			s = s.substr(s.find('/') + 1);// 更新路径,去除已处理的目录名
			// 在当前目录的子目录中查找目标目录
			bool found = false;
			for (auto& subdir : currentNode->children) {
				if (subdir->name == nextDir) {
					currentNode = subdir;
					found = true;
					break;
				}
			}
			// 如果未找到目标目录,则返回当前节点
			if (!found) {
				return;
			}
		}
		for (const auto& filename : currentNode->files) {// 如果找到同名文件,则说明已存在,无需再创建
			if (filename == s) return; // 直接返回,不进行任何操作
		}
		currentNode->files.push_back(s);
		sort(currentNode->files.begin(), currentNode->files.end());//对当前目录下的文件列表按照字典序进行排序
	}
}

Delete函数,其功能为删除目录或者文件,先判断str中是否有‘/’,如果没有则是相对路径直接在当前目录中找是否有名字相同的文件和子目录,如果有则删掉。如果是绝对路径则需要和mkfile函数一样,找到最后子目录,最终的目录项的名字如果和当前目录的名字一样则先返回到父目录,再删除此目录项。如果不一样,则直接删除名字相同的文件和子目录。

void CatalogTree::Delete(string s){ 
	if (s.find('/') == string::npos) {//删除(当前目录下)名为s的目录或文件,将本目录下的子目录和文件访问一遍即可
		for (auto it = now->children.begin(); it != now->children.end(); it++) {
			if ((*it)->name == s) {
				now->children.erase(it);
				break;
			}
		}
		for (auto it = now->files.begin(); it != now->files.end(); it++) {
			if ((*it) == s) {
				now->files.erase(it);
				break;
			}
		}
	}
	else {
		s = s.substr(1);// 去除路径中的第一个字符 '/',因为根节点不需要匹配
		Node* currentNode = root;
		while (s.find('/') != string::npos) {// 循环直到路径中不再包含 '/'
			string nextDir = s.substr(0, s.find('/'));//得到该目录下的下一个子目录的名称
			s = s.substr(s.find('/') + 1);// 更新路径,去除已处理的目录名
			// 在当前目录的子目录中查找目标目录
			bool found = false;
			for (auto& subdir : currentNode->children) {
				if (subdir->name == nextDir) {
					currentNode = subdir;
					found = true;
					break;
				}
			}
			// 如果未找到目标目录,则返回当前节点
			if (!found) {
				return;
			}
		}
		if (s == now->name) {
			cdfather();
			for (auto it = now->children.begin(); it != now->children.end(); it++) {
				if ((*it)->name == s) {
					now->children.erase(it);
					break;
				}
			}
			for (auto it = now->files.begin(); it != now->files.end(); it++) {
				if ((*it) == s) {
					now->files.erase(it);
					break;
				}
			}
			return;
		}
		// 在最后一个子目录中查找目标目录
		for (auto it = currentNode->children.begin(); it != currentNode->children.end(); it++) {
			if ((*it)->name == s) {
				currentNode->children.erase(it);
				break;
			}
		}
		for (auto it = currentNode->files.begin(); it != currentNode->files.end(); it++) {
			if ((*it) == s) {
				currentNode->files.erase(it);
				break;
			}
		}
	}
}

 Save函数用来保存当前目录树,先建立一个新的输出文件,利用栈从根节点开始深度优先搜索,将每个子目录中的所有子目录和文件输出后再到父目录中找还有其他子目录或者文件再输出,直到栈为空。

void CatalogTree::save(string filename) { // 将目录树结构保存到文件中,使用深度优先搜索(DFS)
	ofstream out(filename + ".txt", ios::out);
	stack<Node*> directoryStack;// 用于在DFS遍历期间跟踪目录的堆栈
	Node* currentNode = root;// 从根节点开始
	for (auto child = currentNode->children.begin(); child != currentNode->children.end(); child++) {// 遍历根节点的直接子节点
		out << "mkdir " << (*child)->name << endl;
		if ((*child)->children.size() != 0 || (*child)->files.size() != 0) directoryStack.push((*child));// 如果子目录有子目录或文件,则将其压入堆栈	
	}
	for (auto file = currentNode->files.begin(); file != currentNode->files.end(); file++) out << "mkfile " << (*file) << endl;// 输出根目录中的每个文件的 mkfile 命令
	// DFS遍历
	while (!directoryStack.empty()) {
		bool found = false;//查找是否有子目录或文件
		for (auto child = currentNode->children.begin(); child != currentNode->children.end(); child++) {
			if ((*child) == directoryStack.top()) {
				currentNode = (*child); // 移动到子目录
				found = true;
				break;
			}
		}
		if (!found) {// 如果当前目录没有子目录,则向上移动目录树
			currentNode = currentNode->father;
			out << "cd .." << endl; // 输出cd ..向上移动一个目录
		}
		else {
			Node* currentDirectory = directoryStack.top();
			directoryStack.pop();
			out << "cd " << currentDirectory->name << endl;// 输出cd命令表示进入当前目录
			for (auto child = currentDirectory->children.begin(); child != currentDirectory->children.end(); child++) {// 输出当前目录的每个子目录的 mkdir 命令
				out << "mkdir " << (*child)->name << endl;				
				if ((*child)->children.size() != 0 || (*child)->files.size() != 0) directoryStack.push((*child));// 如果子目录有子目录或文件,则将其压入堆栈
			}
			for (auto file = currentDirectory->files.begin(); file != currentDirectory->files.end(); file++) out << "mkfile " << (*file) << endl; // 输出当前目录中的每个文件的 mkfile 命令
		}
	}
	out << "quit" << endl;// 输出 quit 命令以表示文件结束
	out.close();
}

 Load函数用来从新文件中加载新的树,读取新文件的内容,将根节点置空,根据新文件的内容重新调用以上函数,构造出新的树。

void CatalogTree::load(string filename) { // 从文件加载目录树结构,文件中只包含 cd..、cd str、mkfile str、mkdir str 操作
	root = new Node("");// 创建根节点并设置当前节点为根节点
	now = root;
	ifstream in(filename + ".txt", ios::in);// 打开文件进行读取
	while (true) {
		string line;
		getline(in, line);
		if (line == "quit") {// 如果读取到 "quit",表示文件结束,退出循环
			return;
		}
		else if (line == "cd ..") {
			cdfather(); // 返回上一级目录
		}
		else if (line.substr(0, 2) == "cd") {
			string directoryName = line.substr(3); // 提取目录名
			cdstr(directoryName); // 进入指定名称的子目录
		}
		else if (line.substr(0, 5) == "mkdir") {
			string directoryName = line.substr(6); // 提取目录名
			mkdir(directoryName); // 在当前目录下创建指定名称的子目录
		}
		else if (line.substr(0, 6) == "mkfile") {
			string fileName = line.substr(7); // 提取文件名
			mkfile(fileName); // 在当前目录下创建指定名称的文件
		}
	}
	in.close();
}

 主函数中所有命令均从opfile.txt文件中读出,输出结果在outputfile.txt文件中。

int main() {
	ifstream infile("opfile.txt", ios::in); // 打开输入文件流
	CatalogTree C("outputfile.txt"); // 创建目录树对象
	while (true) {
		string s;
		getline(infile, s); // 从文件中读取一行内容
		if (s == "quit") { // 如果读取到 "quit",则结束循环
			break;
		}
		else if (s == "dir") { // 列出当前目录下的所有目录项
			C.dir();
		}
		else if (s == "cd") { // 打印当前目录的绝对路径
			C.cdap();
		}
		else if (s == "cd ..") { // 切换到当前目录的父目录
			C.cdfather();
		}
		else if (s.substr(0, 2) == "cd") { // 切换到指定路径的目录
			string directoryPath = s.substr(3);
			C.cdstr(directoryPath);
		}
		else if (s.substr(0, 5) == "mkdir") { // 在当前目录下创建子目录
			string directoryName = s.substr(6);
			C.mkdir(directoryName);
		}
		else if (s.substr(0, 6) == "mkfile") { // 在当前目录下创建文件
			string fileName = s.substr(7);
			C.mkfile(fileName);
		}
		else if (s.substr(0, 6) == "delete") { // 删除文件或目录
			string itemName = s.substr(7);
			C.Delete(itemName);
		}
		else if (s.substr(0, 4) == "save") { // 将目录树结构保存到文件
			string saveFileName = s.substr(5);
			C.save(saveFileName);
		}
		else if (s.substr(0, 4) == "load") { // 从文件加载目录树结构
			string loadFileName = s.substr(5);
			C.load(loadFileName);
		}
	}
	return 0;
}

测试结果 

完整代码

#include<iostream>
#include<vector>
#include<fstream>
#include<string>
#include <algorithm>
#include <stack>
#include<queue>
using namespace std;
struct Node {
	string name;
	vector<Node*> children;             //当前目录的子目录
	vector<string> files;            //当前目录的文件
	Node* father;
	Node(string s, Node* p1 = NULL) {
		name = s;
		father = p1;
	}
};
bool judge(const Node* p1, const Node* p2) {//运算符重载,按照字典序排序
	return p1->name < p2->name;
}
class CatalogTree {
private:
	ofstream outfile;
	Node* root;//根目录
	Node* now;//记录访问的目录位置
public:
	CatalogTree(string s) {
		outfile.open(s, ios::out);
		root = new Node("");
		now = root;
	}
	void dir() {//列出当前目录下的所有目录项(子目录和文件)
		for (const auto& file : now->files) outfile << '*' << file << '*' << endl;// 输出当前目录的文件
		for (const auto& subdir : now->children) outfile << subdir->name << endl;// 输出当前目录的子目录
	}
	void cdap();
	void cdfather() {//当前目录变为当前目录的父目录
		if (now != root) { // 如果当前目录不是根目录,则变更当前目录为其父目录
			now = now->father;
		}
		// 如果当前目录是根目录,则不做任何变更
	}
	void cdstr(string s);
	void mkdir(string s);
	void mkfile(string s);
	void Delete(string s);
	void save(string s);
	void load(string s);
};
void CatalogTree::cdap() {//打出当前目录的绝对路经
	if (now == root) { // 如果当前节点为根节点,则输出根目录符号并返回
		outfile << '/' << endl;
		return;
	}
	string absolutePath = ""; // 存储绝对路径的字符串
	Node* currentNode = now;
	while (currentNode != root) {// 遍历直到根节点,将每个节点的名称添加到路径字符串中
		absolutePath = "/" + currentNode->name + absolutePath;
		currentNode = currentNode->father;
	}
	outfile << absolutePath << endl;// 输出绝对路径
}
void CatalogTree::cdstr(string path) {
	if (path.find('/') == string::npos) {//当前目录变为s所表示路径的目录,s为该目录下的一个子目录,    
		for (auto subdir : now->children) {// 遍历当前目录下的所有子目录
			if (subdir->name == path) { // 如果找到目标子目录
				now = subdir; // 将当前目录更改为目标子目录
				return; // 结束循环
			}
		}
		// 如果未找到目标子目录,则保持当前目录不变
	}
	else {//path为从根节点开始的绝对路径,找到最终的目标目录项,将其改为当前目录
		path = path.substr(1);// 去除路径中的第一个字符 '/',因为根节点不需要匹配
		Node* currentNode = root;
		while (path.find('/') != string::npos) {// 循环直到路径中不再包含 '/'
			string nextDir = path.substr(0, path.find('/'));//得到该目录下的下一个子目录的名称
			path = path.substr(path.find('/') + 1);// 更新路径,去除已处理的目录名
			// 在当前目录的子目录中查找目标目录
			bool found = false;
			for (auto& subdir : currentNode->children) {
				if (subdir->name == nextDir) {
					currentNode = subdir;
					found = true;
					break;
				}
			}
			// 如果未找到目标目录,则返回当前节点
			if (!found) {
				now = currentNode;
				return;
			}
		}
		for (auto subdir : currentNode->children) {// 在最后一个子目录中查找目标目录
			if (subdir->name == path) {
				now = subdir;
				return;
			}
		}
		now = currentNode;// 如果未找到目标目录,则返回当前节点
	}
}
void CatalogTree::mkdir(string s){//在(当前目录下)创建一个子目录(名为 s)
	if (s.find('/') == string::npos) {
		for (auto& child_node : now->children) {// 检查当前节点的子节点是否已存在同名节点
			if (child_node->name == s) return;// 如果找到同名节点,则说明已存在,无需再创建
		}
		Node* new_node = new Node(s, now);
		now->children.push_back(new_node);
		if (!now->children.empty()) sort(now->children.begin(), now->children.end(), judge);//对当前节点的子节点按字典序进行排序
	}
	else {
		s = s.substr(1);// 去除路径中的第一个字符 '/',因为根节点不需要匹配
		Node* currentNode = now;
		while (s.find('/') != string::npos) {// 循环直到路径中不再包含 '/'
			string nextDir = s.substr(0, s.find('/'));//得到该目录下的下一个子目录的名称
			 s = s.substr(s.find('/') + 1);// 更新路径,去除已处理的目录名
			for (auto& subdir : currentNode->children) {// 在当前目录的子目录中查找是否已存在同名节点
				if (subdir->name == nextDir) return;
			}
			Node* new_node = new Node(nextDir, currentNode);
			currentNode->children.push_back(new_node);
			if (!currentNode->children.empty()) sort(currentNode->children.begin(), currentNode->children.end(), judge);//对当前节点的子节点按字典序进行排序
			currentNode = new_node;
		}
		for (auto subdir : currentNode->children) {// 在最后一个子目录中查找目标目录
			if (subdir->name == s) return;
		}
		Node* new_node = new Node(s, currentNode);
		currentNode->children.push_back(new_node);
		if (!currentNode->children.empty()) sort(currentNode->children.begin(), currentNode->children.end(), judge);//对当前节点的子节点按字典序进行排序
	}
}
void CatalogTree::mkfile(string s){//在(当前目录下)创建一个文件(名为 s) 
	if (s.find('/') == string::npos) {
		for (const auto& filename : now->files) {// 如果找到同名文件,则说明已存在,无需再创建
			if (filename == s) return; // 直接返回,不进行任何操作
		}
		now->files.push_back(s);
		sort(now->files.begin(), now->files.end());//对当前目录下的文件列表按照字典序进行排序
	}
	else {
		s = s.substr(1);// 去除路径中的第一个字符 '/',因为根节点不需要匹配
		Node* currentNode = root;
		while (s.find('/') != string::npos) {// 循环直到路径中不再包含 '/'
			string nextDir = s.substr(0, s.find('/'));//得到该目录下的下一个子目录的名称
			s = s.substr(s.find('/') + 1);// 更新路径,去除已处理的目录名
			// 在当前目录的子目录中查找目标目录
			bool found = false;
			for (auto& subdir : currentNode->children) {
				if (subdir->name == nextDir) {
					currentNode = subdir;
					found = true;
					break;
				}
			}
			// 如果未找到目标目录,则返回当前节点
			if (!found) {
				return;
			}
		}
		for (const auto& filename : currentNode->files) {// 如果找到同名文件,则说明已存在,无需再创建
			if (filename == s) return; // 直接返回,不进行任何操作
		}
		currentNode->files.push_back(s);
		sort(currentNode->files.begin(), currentNode->files.end());//对当前目录下的文件列表按照字典序进行排序
	}
}
void CatalogTree::Delete(string s){ 
	if (s.find('/') == string::npos) {//删除(当前目录下)名为s的目录或文件,将本目录下的子目录和文件访问一遍即可
		for (auto it = now->children.begin(); it != now->children.end(); it++) {
			if ((*it)->name == s) {
				now->children.erase(it);
				break;
			}
		}
		for (auto it = now->files.begin(); it != now->files.end(); it++) {
			if ((*it) == s) {
				now->files.erase(it);
				break;
			}
		}
	}
	else {
		s = s.substr(1);// 去除路径中的第一个字符 '/',因为根节点不需要匹配
		Node* currentNode = root;
		while (s.find('/') != string::npos) {// 循环直到路径中不再包含 '/'
			string nextDir = s.substr(0, s.find('/'));//得到该目录下的下一个子目录的名称
			s = s.substr(s.find('/') + 1);// 更新路径,去除已处理的目录名
			// 在当前目录的子目录中查找目标目录
			bool found = false;
			for (auto& subdir : currentNode->children) {
				if (subdir->name == nextDir) {
					currentNode = subdir;
					found = true;
					break;
				}
			}
			// 如果未找到目标目录,则返回当前节点
			if (!found) {
				return;
			}
		}
		if (s == now->name) {
			cdfather();
			for (auto it = now->children.begin(); it != now->children.end(); it++) {
				if ((*it)->name == s) {
					now->children.erase(it);
					break;
				}
			}
			for (auto it = now->files.begin(); it != now->files.end(); it++) {
				if ((*it) == s) {
					now->files.erase(it);
					break;
				}
			}
			return;
		}
		// 在最后一个子目录中查找目标目录
		for (auto it = currentNode->children.begin(); it != currentNode->children.end(); it++) {
			if ((*it)->name == s) {
				currentNode->children.erase(it);
				break;
			}
		}
		for (auto it = currentNode->files.begin(); it != currentNode->files.end(); it++) {
			if ((*it) == s) {
				currentNode->files.erase(it);
				break;
			}
		}
	}
}
void CatalogTree::save(string filename) { // 将目录树结构保存到文件中,使用深度优先搜索(DFS)
	ofstream out(filename + ".txt", ios::out);
	stack<Node*> directoryStack;// 用于在DFS遍历期间跟踪目录的堆栈
	Node* currentNode = root;// 从根节点开始
	for (auto child = currentNode->children.begin(); child != currentNode->children.end(); child++) {// 遍历根节点的直接子节点
		out << "mkdir " << (*child)->name << endl;
		if ((*child)->children.size() != 0 || (*child)->files.size() != 0) directoryStack.push((*child));// 如果子目录有子目录或文件,则将其压入堆栈	
	}
	for (auto file = currentNode->files.begin(); file != currentNode->files.end(); file++) out << "mkfile " << (*file) << endl;// 输出根目录中的每个文件的 mkfile 命令
	// DFS遍历
	while (!directoryStack.empty()) {
		bool found = false;//查找是否有子目录或文件
		for (auto child = currentNode->children.begin(); child != currentNode->children.end(); child++) {
			if ((*child) == directoryStack.top()) {
				currentNode = (*child); // 移动到子目录
				found = true;
				break;
			}
		}
		if (!found) {// 如果当前目录没有子目录,则向上移动目录树
			currentNode = currentNode->father;
			out << "cd .." << endl; // 输出cd ..向上移动一个目录
		}
		else {
			Node* currentDirectory = directoryStack.top();
			directoryStack.pop();
			out << "cd " << currentDirectory->name << endl;// 输出cd命令表示进入当前目录
			for (auto child = currentDirectory->children.begin(); child != currentDirectory->children.end(); child++) {// 输出当前目录的每个子目录的 mkdir 命令
				out << "mkdir " << (*child)->name << endl;				
				if ((*child)->children.size() != 0 || (*child)->files.size() != 0) directoryStack.push((*child));// 如果子目录有子目录或文件,则将其压入堆栈
			}
			for (auto file = currentDirectory->files.begin(); file != currentDirectory->files.end(); file++) out << "mkfile " << (*file) << endl; // 输出当前目录中的每个文件的 mkfile 命令
		}
	}
	out << "quit" << endl;// 输出 quit 命令以表示文件结束
	out.close();
}
void CatalogTree::load(string filename) { // 从文件加载目录树结构,文件中只包含 cd..、cd str、mkfile str、mkdir str 操作
	root = new Node("");// 创建根节点并设置当前节点为根节点
	now = root;
	ifstream in(filename + ".txt", ios::in);// 打开文件进行读取
	while (true) {
		string line;
		getline(in, line);
		if (line == "quit") {// 如果读取到 "quit",表示文件结束,退出循环
			return;
		}
		else if (line == "cd ..") {
			cdfather(); // 返回上一级目录
		}
		else if (line.substr(0, 2) == "cd") {
			string directoryName = line.substr(3); // 提取目录名
			cdstr(directoryName); // 进入指定名称的子目录
		}
		else if (line.substr(0, 5) == "mkdir") {
			string directoryName = line.substr(6); // 提取目录名
			mkdir(directoryName); // 在当前目录下创建指定名称的子目录
		}
		else if (line.substr(0, 6) == "mkfile") {
			string fileName = line.substr(7); // 提取文件名
			mkfile(fileName); // 在当前目录下创建指定名称的文件
		}
	}
	in.close();
}
int main() {
	ifstream infile("opfile.txt", ios::in); // 打开输入文件流
	CatalogTree C("outputfile.txt"); // 创建目录树对象
	while (true) {
		string s;
		getline(infile, s); // 从文件中读取一行内容
		if (s == "quit") { // 如果读取到 "quit",则结束循环
			break;
		}
		else if (s == "dir") { // 列出当前目录下的所有目录项
			C.dir();
		}
		else if (s == "cd") { // 打印当前目录的绝对路径
			C.cdap();
		}
		else if (s == "cd ..") { // 切换到当前目录的父目录
			C.cdfather();
		}
		else if (s.substr(0, 2) == "cd") { // 切换到指定路径的目录
			string directoryPath = s.substr(3);
			C.cdstr(directoryPath);
		}
		else if (s.substr(0, 5) == "mkdir") { // 在当前目录下创建子目录
			string directoryName = s.substr(6);
			C.mkdir(directoryName);
		}
		else if (s.substr(0, 6) == "mkfile") { // 在当前目录下创建文件
			string fileName = s.substr(7);
			C.mkfile(fileName);
		}
		else if (s.substr(0, 6) == "delete") { // 删除文件或目录
			string itemName = s.substr(7);
			C.Delete(itemName);
		}
		else if (s.substr(0, 4) == "save") { // 将目录树结构保存到文件
			string saveFileName = s.substr(5);
			C.save(saveFileName);
		}
		else if (s.substr(0, 4) == "load") { // 从文件加载目录树结构
			string loadFileName = s.substr(5);
			C.load(loadFileName);
		}
	}
	return 0;
}

如能打赏,不胜感激[叩谢]。

  • 31
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值