目录

  • 一、功能
  • 二、分析
  • 三、代码
  • 四、运行截图
  • 五、总结
  • 六、源代码

一、功能

二、分析

首先,该目录管理系统提供的源数据是一个二进制的.dat文件,可随机文件操作。如图,采用 fread 函数读出后,每一行有三列,分别代表分类号、分类名、双亲结点分类号。

采用结构体数组存储文件内容。由于 “输出树形分类树” 功能,采用双亲表示法 实现 N叉树的顺序存储。

  • 增加分类:将新增记录写入文件尾部,同时更新数组;
  • 输出分类:打印数组即可;
  • 输出树形分类树:对N叉树进行先序遍历,输出的树有层次感,是利用树的层数实现的;
  • 修改分类:对文件进行随机文件操作 (先fseek,后fwrite),同时更新数组;
  • 删除分类:将 “###” 覆写 (fwrite) 待删除记录和数组元素。

三、代码

//目录管理系统:双亲表示法实现N叉树的顺序存储,编译环境vs 2019
#define _CRT_SECURE_NO_WARNINGS
#define MAX_TREE_SIZE 100           //最多结点个数
#include <stdio.h>
#include <iostream>
using namespace std;typedef struct Catalog              //目录结构类型
{char key[20] = { 0 };         //编号char caption[80] = { 0 };      //类名char parent[20] = { 0 };       //父亲编号
}Catalog;typedef struct CatalogTree
{Catalog Node[MAX_TREE_SIZE] = { 0 };  //数据域,Node的下标就表示了某个单元信息在顺序存储中的位置int root = 0;                  //根节点位置,根节点默认在0号单元int num = 0;                  //最后一个结点的数组编号
}CatalogTree;CatalogTree tree, * ct;                //如果直接用tree接收,应该写作fwrite(&tree->Node[i],sizeof(Catalog),1,fp)
FILE* fp = fopen("catalog3.dat", "rb+");//文件指针
Catalog C = { "###","###","###" };   //初始化一个结构体
int t = 0;                         //记录层数
int f = 0;                         //存储下标void CreateCatalogTree();         //0 将文件读入数组
void AddCatalog();                  //1 增加分类信息,保存至文件
void AlterCatalog();                //2 修改分类信息,保存至文件
void DeleteCatalog();               //3 从文件中删除分类信息
void PrintCatalog();                //4 顺序输出文件中所有分类信息
void CreateCatalogTree();           //5.1 将分类信息用双亲表示法存储。调用PrintCatalogTree()
void PrintCatalogTree();            //5.2 调用PreOrder()函数void PreOrder(CatalogTree* ct, int i);
void PostOrder(CatalogTree* ct, int i);
bool leaf(CatalogTree* ct, int i);void menu();int main()
{if (fp == NULL){printf("文件打开失败\n");return 0;}printf("**116132019188 曾思雅 计四**\n");CreateCatalogTree();            //将.dat文件写入数组for (;;){menu();int choice;scanf("%d", &choice);switch (choice){case 1:AddCatalog(); break;case 2:PrintCatalog(); break;     //顺序输出分类信息case 3:PrintCatalogTree(); break; //输出树形分类树,所以能理解为什么有些用&了是吧case 4:AlterCatalog(); break;case 5:DeleteCatalog(); break;default:return 0;}}fclose(fp);                              //关闭文件流return 0;
}/*将文件读入数组*/
void CreateCatalogTree()
{tree.num = 0;                             //初始化结点数量for (int i = 0; !feof(fp); i++)             //如果文件没有读完,一次读入一个Catalog{fread(&tree.Node[i], sizeof(Catalog), 1, fp);       //从文件中读入//  printf("%d\t%s\t%s\t%s\n",i,tree.Node[i].key, tree.Node[i].caption, tree.Node[i].parent);tree.num++;}//但是请注意,会多读入一个空数据tree.num--;//  printf("tree.num=%d",tree.num);
}/*开始写增加分类,支持读入多条记录,保存至文件*/
void AddCatalog()
{char temp[20] = { 0 };printf("请依次输入key、caption、parent(以#结束输入)\n");    //将新增分类直接写入数组for (;;){scanf("%s", temp);if (strcmp(temp, "#") != 0){strcpy(tree.Node[tree.num].key, temp);scanf("%s %s", tree.Node[tree.num].caption, tree.Node[tree.num].parent);   //更新数组//将新增分类写入文件fwrite(&tree.Node[tree.num], sizeof(Catalog), 1, fp);tree.num++;}else break;}printf("添加成功!\n");
}//至此增加分类已经写好/*顺序打印数组*/
void PrintCatalog()
{for (int i = 0; i < tree.num; i++){if (strcmp(tree.Node[i].caption, "###") != 0)printf("%s\t%s\t%s\n", tree.Node[i].key, tree.Node[i].caption, tree.Node[i].parent);}
}/*树形打印分类树,调用先序遍历实现*/
void PrintCatalogTree()
{   //调用先序遍历PreOrder(&tree, 0);
}/*先序遍历*/
void PreOrder(CatalogTree* ct, int i)           //输出树形分类树,这里为什么得*ct呢?*ct是树的数组
{//t为对应层数,打印空格for (int i = 0; i < t; i++)printf("   ");printf("|--%s  %s\n", tree.Node[i].caption, tree.Node[i].key);for (int j = i + 1; j < tree.num; j++)     //对于结点i,需要和后面的结点比较{  //在数组中逐个比对找到结点i的所有子结点if (strcmp(ct->Node[j].parent, ct->Node[i].key) == 0){t++;PreOrder(ct, j);t--;}}
}/*删除分类:包括级联删除和叶子结点删除,级联删除调用后序遍历*/
void DeleteCatalog()
{char a[20] = { 0 };int c;printf("请输入您需要删除的key: ");scanf("%s", &a);                        //用户输入的应当是一个keyfor (f = 0; f < tree.num; f++){//对于每个结点来说if (strcmp(tree.Node[f].key, a) == 0)       //找到该结点,进一步删除隶属于它的结点break;}printf("待删除信息:%s   %s   %s\n", tree.Node[f].key, tree.Node[f].caption, tree.Node[f].parent);if (leaf(&tree, f)){printf("tip:该目录下有子目录,将一起删除。\n如果继续删除操作,请按1;否则按2:");scanf("%d", &c);if (c != 1){printf("退出删除\n");return;}}PostOrder(&tree, f);printf("删除成功!\n");
}/*后序遍历*/
void PostOrder(CatalogTree* ct, int i)          //输出树形分类树,这里为什么得*ct呢?*ct是树的数组
{for (int j = i + 1; j < tree.num; j++)      //对于结点i,需要和后面的结点比较{  //在数组中逐个比对找到结点i的所有子结点if (strcmp(ct->Node[j].parent, ct->Node[i].key) == 0){t++;PostOrder(ct, j);t--;}}//覆盖文件fseek(fp, i * sizeof(Catalog), 0);fwrite(&C, sizeof(Catalog), 1, fp);//将对应tree.Node[f]置为空,这样文件和数组就相互对应了strcpy(tree.Node[i].key, "###");strcpy(tree.Node[i].caption, "###");strcpy(tree.Node[i].parent, "###");
}/*叶子结点判断,返回布尔型*/
bool leaf(CatalogTree* ct, int i)
{for (int j = i + 1; j < tree.num; j++)      //对于结点i,需要和后面的结点比较{  //在数组中逐个比对找到结点i的所有子结点if (strcmp(ct->Node[j].parent, ct->Node[i].key) == 0)return true;}return false;
}/*修改分类:如果是叶子结点,可修改整条记录;否则只能修改caption*/
void AlterCatalog()
{printf("请输入您需要修改的key: ");int f = 0;                                     //存储下标char a[80] = { 0 };scanf("%s", &a);                                //用户输入的应当是一个keyfor (f = 0; f < tree.num; f++)                 //对于每个结点来说{if (strcmp(tree.Node[f].key, a) == 0)      //找到该结点break;}//读入待修改信息,写入数组printf("待修改信息:%s   %s   %s\n", tree.Node[f].key, tree.Node[f].caption, tree.Node[f].parent);if (leaf(&tree, f) == false)                      //是叶子结点{printf("请输入key,caption,parent:");scanf("%s%s%s", tree.Node[f].key, tree.Node[f].caption, tree.Node[f].parent); //已经写入数组}else{printf("tip:由于该目录下还有子目录,所以只能修改caption。\n请输入caption:");scanf("%s", tree.Node[f].caption);}//更新文件fseek(fp, f * sizeof(Catalog), 0);fwrite(&tree.Node[f], sizeof(Catalog), 1, fp);printf("修改成功!\n");
}/*打印菜单*/
void menu()
{printf("\n************菜单************\n");printf("1、增加分类\n2、输出分类\n3、输出树形分类树\n4、修改分类\n5、删除分类\n0、返回上级\n");printf("****************************\n");printf("请输入你的选择(0-5):");
}

四、运行截图

五、总结

1、当看到树形分类树有层次的输出时,我感受到了心流。按照PPT,在草稿纸上边想边画,真的就实现了树的双亲表示法,还递归实现了先序遍历,利用层数打印空格体现层次。
2、另外,考虑到非叶子结点的改动可能会影响到其子结点等,所以将删除功能分为级联删除和叶子结点删除,将修改分为叶子结点修改和非叶子结点修改。而级联删除是通过后序遍历实现的。
3、memset对结构体数组不管用。
4、结构体数组在使用过程中,不可以直接{" "}赋值。

5、在vs编写程序过程中发现,.-> 是不一样的。

六、源代码

GitHub源码

分类目录管理系统——软件开发项目实践相关推荐

  1. 浅析软件开发项目中的需求分析

    [摘要]在软件开发项目中,需求分析是关乎软件项目开发成败的重要因素.现在的软件项目中返工开销占了总开销很大比例,而导致返工的主要原因是需求分析不明确.针对这一情况,文章阐述了软件开发中需求分析任务.需 ...

  2. 承接各类软件开发项目

    承接各类软件开发项目.测试项目.培训教学 尊敬的各位来访者,您好! 首先感谢你对在下的关注,我的发展离不开各位的支持与厚爱. 本人的开发技术生涯伴随微软.NET平台发展已有近十年.所谓"知之 ...

  3. 软件开发项目风险管理的几点体会

     参与过大型软件项目的人都会认识到许多事情都可能出错,一但出错就可能给项目带来危害.损失或其它不利影响.风险是在项目中发生的一系列事件或不利结果的可能性.软件开发是一项 高风险的活动,在项目开发过 ...

  4. 软件开发综合实践实习小结

    软件开发综合实践实习小结 前言: 这次大二的小学期很是不一样,非常强烈的感受到了动手能力的重要性,为期半个月的专题实训教会了我知识,技能,方法等各方面的内容.很幸运能够遇到这位非常耐心,声音又好听的美 ...

  5. 软件开发项目 质量管理的6大关键事项

    1.项目进度的质量保证 在保证项目进度前,需要保证项目开发计划尽可能合理.合理的项目计划,有助于项目进度的顺利进行. 另外,为了提高团队的整个任务分配效率,CoCode自主研发出"自动规划& ...

  6. 软件开发项目指标_重要的软件开发指标

    软件开发项目指标 作为一个行业,我们在衡量我们所做的工作以及做得如何出色方面做得非常差. 除了少数组织购买了昂贵的重量级模型(如CMMI或TSP / PSP(全部都是在微观水平上进行测量)或6 Sig ...

  7. 软件开发项目影响进度因素及控制浅谈

    一.影响软件开发项目进度的因素 要有效地进行进度控制,必须对影响进度的因素进行分析,事先或及时采取必要的措施,尽量缩小计划进度与实际进度的偏差,实现对项目的主动控制.软件开发项目中影响进度的因素很多, ...

  8. 声光报警器 | 在软件开发项目中加入声光告警、语音通知方案

    在IT软件开发项目中,有的客户有这样的需求,希望在公司的生产环境中安装硬件设备,比如声光报警灯.语音通知设备等.当软件中的某些重要信息或者异常事件发生时,向设备发送消息,通过警示音.灯光.语音三个维度 ...

  9. 软件开发项目的风险管理 (转)

    原作者:李艺兰 软件开发项目的风险管理 众所周知,软件开发过程可分为:需求分析.设计.编码.测试.安装及维护等几个过程(在RUP方法中:业务建模.需求.分析设计.实施.测试.部署),实际上一个完整的软 ...

  10. 软件开发项目文档模版

    这是一份软件开发项目的文档模版,编写给有需要的人. XX系统开发 开发团队: 组长: 组员: 备注:广州大学华软软件学院 目录 XX系统开发.............................. ...

最新文章

  1. 某阿里程序员爆料自己的p9领导:每天炒股喝茶开会!羡慕嫉妒恨!以后要当领导!网友:不在其位,不知其累!...
  2. 数据结构及算法基础--优先队列(Priority Queue)
  3. nginx php 错误日志,PHP 错误与异常的日志记录
  4. 网卡驱动程序之编写虚拟网卡(二)
  5. springmvc图片文件上传接口
  6. 有效的MongoDB索引
  7. android地图定位到海洋,GPS定位技术进行高精度海洋定位的应用
  8. 电脑软件上的按钮原来是这样来的:按钮组件
  9. php链接mysql数据库
  10. 蓝牙耳机自动接听软件_使用蓝牙耳机接听Skype呼叫
  11. MFC 的List Control控件实现可编辑
  12. Go初体验-实现平方根函数
  13. PHP读取txt文件自动分成指定行数
  14. 第一次初学游泳+自我总结+小窍门
  15. 微服务是银弹还是焦油坑?
  16. OneNote中英文格式不同,OneNote无法修改英文字体,OneNote默认英文字体为Calibri无法修改的问题。
  17. 新浪微博网页版打开异常解决方案
  18. 2016.3.16__CSS3_选择器_边框_背景_蒙版mask__第九天
  19. Linux报错:-bash: 路径xx: No such file or directory解决方法
  20. python两张图合成一张_Python图像处理实现两幅图像合成一幅图像的方法【测试可用】...

热门文章

  1. Rust: 属性(attribute)的含义及文档大全
  2. 机构、基民双输,基金销售的利益困局如何破?
  3. 中基协会长洪磊:尽快制定大类资产配置管理办法 推非保本理财转型
  4. 经纬创投:我们研究了200多家公司的融资条款,告诉你如何防止被“套路”
  5. Sentinel 实战应用中的小技巧
  6. 数据湖生态联盟正式成立
  7. Java画韦恩图_R绘制韦恩图 | Venn图
  8. 【回归预测】基于matlab麻雀算法优化相关向量机RVM回归预测【含Matlab源码 1750期】
  9. 【人脸识别】基于matlab GUI FISHER人脸识别【含Matlab源码 605期】
  10. 【CVRP】基于matlab节约算法求解带容量的车辆路径规划问题【含Matalb源码 157期】