C语言_链表实现目录

目录结构体的构思过程

1、确定单个目录的结构

参考Windows下的目录形式,一个目录有4部分进行链接:上级目录、下级目录、同级左目录、同级右目录。因此一个目录的基本链表形式应该如下图:

这上下左右四个节点,并不是一定要有链接值。如果没有链接,在代码中的体现就是指针指向NULL。

2、确定多级目录的结构

有了单个目录的形式,就可以将他们串起来,形成一个多级目录。这里采用的方法是:将上级目录只链接一个下级目录,结构如下:

3、确定需要做的功能

模仿Linux相关指令,本文将实现如下几种功能: 

mkdir创建目录
rmdir 删除目录
ls              列出子目录
pwd显示当前目录
cd查找指定目录、跳转到指定目录

目录功能的C语言实现

1、创建目录(增)

目录是一个链表,因此所要首先声明一个目录的结构体。

struct dir {
	char name[128];         /* 目录的名字 */
	struct dir *up_next;    /* 链接上级目录 */
	struct dir *down_next;  /* 链接下级目录 */
	struct dir *left_next;  /* 链接同级左级目录 */
	struct dir *right_next; /* 链接同级右级目录 */
};

创建目录分为两步:

  • 使用malloc给结构体分配一个空间,并对参数进行初始化(指针都指向NULL)
  • 对目录进行链接、名称有效性判断

1.1 编写开辟空间函数

开辟空间步骤就两步:

  • malloc开辟空间,并检查是否开辟成功,如果malloc失败会返回NULL
  • 初始化成员列表,数组使用memset清零,指针全部指向NULL

具体的代码实现如下:

/* 创建1:malloc开辟空间 */
struct dir* Dir_Create_NewLink(void){

	struct dir * newlink = NULL;
	
	//开辟空间,开辟失败返回NULL 
	newlink = (struct dir *)malloc(sizeof(struct dir));
	if(newlink == NULL){
		printf("malloc failed\n");
		return NULL;
	}
	
	//初始化
	memset(newlink->name,'\0',128); 
	newlink->up_next = NULL;
	newlink->down_next = NULL;
	newlink->left_next = NULL;
	newlink->right_next = NULL;
	
	return newlink;		
} 

1.2 编写目录创建函数

目录创建也需要两步:

  • 对结构体的上下左右指针进行链接。
  • 对名称进行有效性判断,并赋值给结构体的name数组中

具体代码如下:

注意:该代码的名称判断有效性中的重名检测函数并未编写,重名检测所用到的函数将在后面编写

/* 创建2:链接目录、对目录进行命名 */ 
void Dir_Create_NewDir(struct dir* now_dir,struct dir* new_dir,struct dir* root_dir,char* file_name){
	
	struct dir* pointTem;
	struct dir* point = now_dir;
	char new_name[128];/* 用于暂存目录名字 */
	char* point_name = NULL; 
	
	/* 传入参数有效性判断 */
	if(now_dir == NULL){
		printf("now_dir is NULL\n");
		return;
	}
	if(new_dir == NULL){
		printf("new_dir is NULL\n");
		return;
	}
	if(root_dir == NULL){
		printf("root_dir is NULL\n");
		return;
	}
	if(file_name == NULL){
		printf("file_name is NULL\n");
		return;
	} 
	
	//1.链接
	//例如当前为根 \ ,则创建的目录为 \abc
		//1.1上级链接,新节点为当前节点的子目录 
		new_dir->up_next = now_dir;//上级目录是当前位置
		//1.2下 指NULL,新建的目录没有子目录 
		new_dir->down_next = NULL;
		//1.3将新目录插入到上级目录链接中:
			//1.3.1 当前目录没有子目录,则这作为第一个子目录,即子目录的头
			//point指向的是new_dir 
			if(point->down_next == NULL){
				point->down_next = new_dir;
			}
			//1.3.2如果有子目录,遍历目录链表,进行尾插
			else{	
				point = point->down_next;//point指向子目录的头 
				while(point != NULL){//遍历子目录同级链表 
					pointTem = point;
					point = point->right_next;
				}
				pointTem->right_next = new_dir;/* 最后一个链表的右边为新链表 */
				new_dir->left_next = pointTem;/* 新链表的左边为原来最后一个链表 */ 
			}
	
	//2.名字处理 
		//2.1有效性判断
		strcpy(new_name,file_name);
		/* 第一个字符应该为 '\' */ 
		if(new_name[0]!='\\'){	
			printf("name error,first is not \\ \n");
			free(new_dir);
			return;
		}
		/* 滤掉第一个 '\',看之后是否还有 '\' */
		point_name = new_name;
		point_name++;
		while(*point_name!=NULL){
		
			if(*point_name == '\\'){
				printf("name error,too many \\ \r\n");
				free(new_dir);
				return;
			}
			point_name++;
		}
		//2.2改名
			//2.2.1如果在根目录后,则保留输入的名字,不进行修改 
			if(strcmp(now_dir->name,"\\")==0){
				//不修改
				strcpy(new_name,file_name);
			}
			//2.2其他目录直接加入名称 
			else{ 
				strcpy(new_name,now_dir->name);//例如当前为 \a  new_name=\a
				strcat(new_name,file_name);//例如新节点为\b new_name=\a\b
			} 
		/*2.3判断文件是否重名 */
		if(Dir_Find_API(root_dir,new_name) != NULL){
			printf("name has existed\n");
			free(new_dir);
			return;	
		}
		//2.4名称没问题,写名 
		strcpy(new_dir->name,new_name);//新节点更名为\a\b		  
}

1.3 编写main测试函数

有了上面两个子函数,下面在main中编写一个测试函数。测试功能为:创建一个根目录 \ ,在根目录下创建文件 \test ,在 \test 下创建 \test2 ,在 \test2 下创建 \test3。

前面创建的两个子函数声明如下:

/* 开辟空间 */
struct dir* Dir_Create_NewLink(void);
/* 创建目录 */
void Dir_Create_NewDir(struct dir* now_dir,struct dir* new_dir,struct dir* root_dir,char* file_name); 

具体代码如下:

int main(){
	
	struct dir* now_dir;
	struct dir* tmp_dir;
	struct dir* root_dir;
	
	//创建根节点 
	root_dir = Dir_Create_NewLink();
	strcpy(root_dir->name,"\\");
	now_dir = root_dir;
	printf("root_dir name:%s\n",root_dir->name);
	
	//创建子目录
	tmp_dir = Dir_Create_NewLink();
	Dir_Create_NewDir(now_dir,tmp_dir,root_dir,(char*)"\\test"); 
	printf("new_dir name:%s\n",tmp_dir->name);
	
	//进入子目录,再创建节点
	now_dir = tmp_dir;
	tmp_dir = Dir_Create_NewLink();
	Dir_Create_NewDir(now_dir,tmp_dir,root_dir,(char*)"\\test2");
	printf("new_dir name:%s\n",tmp_dir->name);
	
	//进入子目录,再创建节点
	now_dir = tmp_dir;
	tmp_dir = Dir_Create_NewLink();
	Dir_Create_NewDir(now_dir,tmp_dir,root_dir,(char*)"\\test3");
	printf("new_dir name:%s\n",tmp_dir->name); 
	
	return 0;
}

2、进入、查询目录(查)

进入的方法就是查询到这个目录的链表位置,之后将当前节点赋值为查到的节点。这样就实现了进入。

在 “1、mkdir” 的 "1.3编写main测试函数 " 中写到  now_dir = tmp_dir,这就是进入一个目录。那么,cd相关函数的任务就是将tmp_dir换成一个函数,并传入想去的路径,返回值赋值为now_dir。

查找的步骤有两步:

  • 得到传入的目录有多少级,就向下查找多少次。(通过记录 ' \ '的个数判断多少级)
  • 进入一级,遍历同级目录,看是否存在名称一样的。如果存在,则返回这个链表指针。

思路图示如下:

2.1 编写获取目录级数函数

获取目录的级数就是遍历传入的目录名,去寻找 ' \ ' 的个数。

这里要注意的是,根目录( \ )和一级目录( \abc )这两个的 ' \ ' 都是 1 个,因此需要对根目录去单独判断,将级数设置为 0 。

具体代码实现如下:

/* 进入1:计算路径多少级 */
char Dir_Find_Name_Level(char* file_name){
	
	char find_cnt=0;
	char* point = file_name;
	
	//1.遍历寻找 '\'的个数 
	while(*point != '\0'){
		
		if(*point == '\\'){
			find_cnt++;
		}
		point++;
	}
	//2.如果是根目录,级数要设为 0 
	if(strcmp(file_name,"\\") == 0){
		find_cnt = 0;
	}
	return find_cnt;	
}

2.2 编写向下搜索指定层函数

向下搜索指定层数思路就是:不断的使得当前节点 = 当前节点的下节点,循环操作指定层数即可到达。

具体代码实现如下:

/* 进入2:向下移动指定层数  */
struct dir* Dir_Find_Down_Level(struct dir* now_node,char find_cnt){
	
	struct dir* point_dir = now_node; 
	char i=0;
	//当前节点 = 下节点。不断向下  
	for(i=0;i<find_cnt;i++){
		point_dir = point_dir->down_next;
	}
	return point_dir;
}

2.3 编写同层搜索指定名函数

同层搜索的思路就是:向右指针进行遍历,查找是否存在一致的文件名

具体的代码实现如下:

/* 进入3: 平层搜索所需文件名  */
struct dir* Dir_Find_Horizon_Name(struct dir* now_dir,char* file_name){
	
	struct dir* point_dir = now_dir;
	struct dir* find_dir = NULL;//用于存储找到的链表位置 
	
	/* 遍历寻找指定名称 */				
	while(point_dir!=NULL){	
		//找到了 
		if(strcmp(point_dir->name,file_name) == 0){
			find_dir = point_dir;
			break;
		}
		point_dir = point_dir->right_next;//平级,就是右指针遍历 
	}
	return find_dir;
}

2.4 编写 查找/进入 函数

查找函数的逻辑就如最先分析的一样。调用前面3个子函数实现相应的功能。

具体的代码如下:

/* 进入4: 进入/查找 文件 */
struct dir* Dir_Find_API(struct dir* root_node,char* file_name){
	
	char find_cnt=0;//向下搜索的层数
	struct dir* point_dir = NULL;
	char i=0; 
	char name_tmp[128];//用数组暂存file_name,因为strtok不能传指针,只能传数组 
	char*point_name = NULL;//存储strtok的返回值 
	char name_find_now[100]="\\";//当前要找的路径 
	
	//1.获取向下搜索层数 
	find_cnt = Dir_Find_Name_Level(file_name);
	
	//2.开始搜索
		//2.1取出第一级目录 
			//2.1.1将file_name的第一个路径取出来 
			strcpy(name_tmp,file_name);
			point_name = strtok(name_tmp,"\\");//注意:strtok不能传指针,只能传数组,因为strtok会改变数组值 
			//2.2.2将取出来的路径,添加到根的后面
			if(point_name!=NULL){
				strcat(name_find_now,point_name);	
			} 
		//2.2一层层搜索
		point_dir = root_node;
		//根节点 find_cnt = 0,不进循环
		//从第一级目录 \a 开始找 
		for(i=0;i<find_cnt;i++){
			
			//2.2.1向下移动 
			point_dir = Dir_Find_Down_Level(point_dir,1); //向下移动 1 层 
			point_dir = Dir_Find_Horizon_Name(point_dir,name_find_now);//平层寻找名称
			
			//2.2.2同层未找到,不找了 
			if(point_dir == NULL){
				break; 
			}
			
			//2.2.3再加一级要寻找的文件名字 
			point_name = strtok(NULL,"\\");
			//printf("f_point = %s\n",f_point);//当找不到时,返回空指针 
			if(point_name!=NULL){
				strcat(name_find_now,"\\");
				strcat(name_find_now,point_name);//strcat不能链接空指针 
			} 
		}
	return point_dir;
} 

2.5 编写main测试函数

在main中编写一个测试函数。测试功能为:首先保留 " 1.mkdir " 中 " 1.3 编写main测试函数 " 的代码,在此基础上去查找一下 \test\test2 是否存在。

前面创建的子函数声明如下:

/* 计算路径多少级 */
char Dir_Find_Name_Level(char* file_name);
/* 向下移动指定层数  */
struct dir* Dir_Find_Down_Level(struct dir* now_dir,char find_cnt);
/* 平层搜索所需文件名  */
struct dir* Dir_Find_Horizon_Name(struct dir* now_dir,char* file_name);
/* 进入/查找 文件 */
struct dir* Dir_Find_API(struct dir* root_node,char* file_name);

具体代码如下:

int main(){
	
	struct dir* now_dir;
	struct dir* tmp_dir;
	struct dir* root_dir;

/* 1.mkdir部分 */	
/* ======================================================================================= */	
	//创建根节点 
	root_dir = Dir_Create_NewLink();
	strcpy(root_dir->name,"\\");
	now_dir = root_dir;
	printf("root_dir name:%s\n",root_dir->name);
	
	//创建子目录
	tmp_dir = Dir_Create_NewLink();
	Dir_Create_NewDir(now_dir,tmp_dir,root_dir,(char*)"\\test"); 
	printf("new_dir name:%s\n",tmp_dir->name);
	
	//进入子目录,再创建节点
	now_dir = tmp_dir;
	tmp_dir = Dir_Create_NewLink();
	Dir_Create_NewDir(now_dir,tmp_dir,root_dir,(char*)"\\test2");
	printf("new_dir name:%s\n",tmp_dir->name);
	
	//进入子目录,再创建节点
	now_dir = tmp_dir;
	tmp_dir = Dir_Create_NewLink();
	Dir_Create_NewDir(now_dir,tmp_dir,root_dir,(char*)"\\test3");
	printf("new_dir name:%s\n",tmp_dir->name);
	
	//测试重名是否可用 
	tmp_dir = Dir_Create_NewLink();
	Dir_Create_NewDir(now_dir,tmp_dir,root_dir,(char*)"\\test3"); 
/* 2.cd部分 */
/* ======================================================================================= */
	 
	tmp_dir = Dir_Find_API(root_dir,(char*)"\\test\\test2\\test3");
	
	if(tmp_dir == NULL){
		printf("dir not find\n");
	}else{
		printf("dir name = %s\n",tmp_dir->name); 	
	}
	
	return 0;
}

3、删除目录(删)

删除目录的思路就是:先将该节点的上下左右的目录进行一些链接,之后调用 free 将所要删除的节点进行释放,这样即可完成目录的删除。

3.1 编写删除目录函数

具体代码实现如下:

void Dir_Del(struct dir* free_dir,struct dir** now_dir,struct dir* root_dir){
	
	struct dir* point_mid=NULL;
	
	//当前目录就是要释放的位置,则当前目录指向根目录 
	if(*now_dir == free_dir){
		*now_dir = root_dir; 
	}
	
	if(free_dir == NULL){
		printf("free_node is NULL\n");
		return; 
	}
	point_mid = free_dir;
	
	//1.链接上下左右 
		//1.1下处理
		//链接不为空,说明有子目录,禁止删除 
		if(point_mid->down_next!=NULL){
			printf("dir is not empty,can't remove \n");
			return;
		}
		//1.2上、左、右处理 
		else{
			//1.2.1上:上方为空,则不用管。上方非空,则需向右转移链接
			if(point_mid->up_next!=NULL){
				point_mid->up_next->down_next = point_mid->right_next;//中的上 链接 中的右
				if(point_mid->right_next!=NULL){
					point_mid->right_next->up_next = point_mid->up_next;//中的右 链接 中的上 
				}	
			}
			//1.2.2左:左方为空,则不用管。左方非空,则需向右边链接 
			if(point_mid->left_next!=NULL){
				point_mid->left_next->right_next = point_mid->right_next;//中的左 链接 中的右
				if(point_mid->right_next!=NULL){
					point_mid->right_next->left_next = point_mid->left_next;//中的右 链接 中的左 
				}
			}
			//1.2.3右:右方为空,则不用管。右方非空,则需向左边链接 
			if(point_mid->right_next!=NULL){
				point_mid->right_next->left_next = point_mid->left_next;//中的右 链接 中的左
				if(point_mid->left_next!=NULL){
					point_mid->left_next->right_next = point_mid->right_next;//中的左 链接 中的右	
				} 	
			}	
		}
		
	//2.释放空间 
	free(free_dir); 		
}  

3.2编写main测试函数

在main中编写一个测试函数。测试功能为:首先保留 " 1.mkdir " 和 " 2.cd "中 " 编写main测试函数 " 的代码,在此基础上删除一个目录,再次查找,观察目录是否依旧存在。

具体代码实现如下:

int main(){
	
	struct dir* now_dir;
	struct dir* tmp_dir;
	struct dir* root_dir;

/* 1.mkdir部分 */	
/* ======================================================================================= */	
	//创建根节点 
	root_dir = Dir_Create_NewLink();
	strcpy(root_dir->name,"\\");
	now_dir = root_dir;
	printf("root_dir name:%s\n",root_dir->name);
	
	//创建子目录
	tmp_dir = Dir_Create_NewLink();
	Dir_Create_NewDir(now_dir,tmp_dir,root_dir,(char*)"\\test"); 
	printf("new_dir name:%s\n",tmp_dir->name);
	
	//进入子目录,再创建节点
	now_dir = tmp_dir;
	tmp_dir = Dir_Create_NewLink();
	Dir_Create_NewDir(now_dir,tmp_dir,root_dir,(char*)"\\test2");
	printf("new_dir name:%s\n",tmp_dir->name);
	
	//进入子目录,再创建节点
	now_dir = tmp_dir;
	tmp_dir = Dir_Create_NewLink();
	Dir_Create_NewDir(now_dir,tmp_dir,root_dir,(char*)"\\test3");
	printf("new_dir name:%s\n",tmp_dir->name);
	
	//测试重名是否可用 
	tmp_dir = Dir_Create_NewLink();
	Dir_Create_NewDir(now_dir,tmp_dir,root_dir,(char*)"\\test3"); 
/* 2.cd部分 */
/* ======================================================================================= */
	 
	tmp_dir = Dir_Find_API(root_dir,(char*)"\\test\\test2\\test3");
	
	if(tmp_dir == NULL){
		printf("dir not find\n");
	}else{
		printf("dir name = %s\n",tmp_dir->name); 	
	}
	
/* 3.rmdir部分 */	
/* ======================================================================================= */
	Dir_Del(tmp_dir,&now_dir,root_dir);
	tmp_dir = Dir_Find_API(root_dir,(char*)"\\test\\test2\\test3"); 
	if(tmp_dir == NULL){
		printf("dir not find\n");
	}else{
		printf("dir name = %s\n",tmp_dir->name); 	
	}
	
	return 0;
}

4、其他简单功能实现

4.1 查找当前所在目录

这个功能就是将当前目录名字打印出来。

具体代码实现如下:

/* 其他简单功能1: 查询当前位置 */
void Dir_pwd(struct dir* now_dir){
	printf("now dir = %s\n",now_dir->name);	
}

4.2列出当前目录的子目录

这个功能就是遍历右指针,打印全部目录的名字

具体代码实现如下:

/* 其他简单功能2: 列出子目录 */
void Dir_ls(struct dir* now_dir){
	
	struct dir* point = now_dir;
	
	if(point->down_next!=NULL){/* 当前目录有子目录 */
		
		point = point->down_next;/* point指向子目录的头 */
		 
		while(point!=NULL){
			printf("%s\n",point->name);
			point = point->right_next;
		}	
	} 
} 

总功能测试main函数编写

具体实现代码如下:

int main(){
	
	struct dir* now_dir = NULL;
	struct dir* tmp_dir = NULL;
	struct dir* root_dir = NULL;
	char buf[128];
	
	//创建根节点 
	root_dir = Dir_Create_NewLink();
	strcpy(root_dir->name,"\\");
	now_dir = root_dir;

	printf("根节点目录:%s\r\n",root_dir->name);
	printf("tset begin\r\n");
	printf("===============================================\r\n");
	printf("请输入你想要做的事情,有以下几种命令\r\n");
	printf("1.mkdir,进行创建新目录\r\n"); 
	printf("2.cd,进入某一个文件\r\n");
	printf("3.rmdir,删除子目录\r\n"); 
	printf("4.pwd,显示当前路径\r\n");
	printf("5.ls,显示子目录\r\n");
	printf("===============================================\r\n");
	while(1){
		
		printf("$RSY ");
		scanf("%s",buf);
		
		/* 1.mkdir */ 
		if(strcmp(buf,"mkdir") == 0){
			
			puts("请输入新目录名字,以\\开头");
			scanf("%s",buf);
			tmp_dir = Dir_Create_NewLink(); 
			Dir_Create_NewDir(now_dir,tmp_dir,root_dir,buf);	
		}
		
		/* 2.cd */
		if(strcmp(buf,"cd") == 0){
			
			puts("请输入需要进入的文件路径");
			scanf("%s",buf);
			
			now_dir = Dir_Find_API(root_dir,(char*)buf);
			if(now_dir == NULL){
				printf("not find dir\n");
			}else{
				printf("find dir name = %s\n",now_dir->name);
			}
		}
		
		/* 3.rmdir */
		if(strcmp(buf,"rmdir") == 0){
			puts("请输入需要删除的文件路径");
			scanf("%s",buf);
			
			tmp_dir = Dir_Find_API(root_dir,(char*)buf);
			if(tmp_dir == NULL){
				printf("not find dir\n");
			}else{
				Dir_Del(tmp_dir,&now_dir,root_dir);
				printf("now dir = %d\n",strlen(now_dir->name));
			}	
		}
		
		/* 4.pwd */
		if(strcmp(buf,"pwd") == 0){
			Dir_pwd(now_dir);
		}
		
		/* 5.ls */
		if(strcmp(buf,"ls") == 0){
			Dir_ls(now_dir);	
		}	
	}
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值