目录结构体的构思过程
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;
}

888

被折叠的 条评论
为什么被折叠?



