家族谱管理系统
一.本程序功能框架图
二 .系统功能
(1) 文件操作功能:记录输入、记录输出、初始化:用户可建立一个家族的族谱保存在文件中。在其后的操作中,可从文件里读取族谱信息、增加新的家族成员、修改已有的家族成员、删除已存在的家族成员。操作完成之后可保存在文件中。
(2) 家谱操作功能:输出家谱,并能查找某人的配偶、所有孩子、所有祖先等功能。
三.系统功能模块设计:
一.问题描述
本课程设计的主要问题是选择一种数据结构来描述家谱中家族成员之间的关系,在此数据结构上加之一些操作,选用特定的算法来实现家谱操作的功能和文件操作的功能。
二.基本要求
设计要求:编写一个程序,采用一棵二叉树表示一个家谱关系。
具体要求:
(1) 文件操作功能:记录输入、记录输出、将家谱记录存盘。
(2) 家谱操作功能:输出家谱,并能查找某人配偶,查找某人所有的儿子,查找某人的所有祖先。
四.概要设计
1.数据结构的设计
在家谱课程设计中采用二叉树来表示家谱关系,由于在家谱中每个家族成员的子女不止一个,而双亲只有一个,所以采用二叉树结构来描述家族成员之间的关系。在家谱课程设计中还用到单链表,在设计中要将二叉树存储在文件中,最终要读取文件中的记录,要将文件中的数据还原到内存中组成二叉树结构,而文件中元素与元素之间的结构是线性,而且直接对文件中的数据操作很不方便,所以将文件中的元素存储在单链表中,再对单链表操作还原成内存中的二叉树。
在对家谱的文件操作中,为了还原家谱方便,采用按层遍历的顺序保存二叉树中各结点的信息,在层次遍历中,使用队列来实现二叉树的层次遍历。
2.算法的设计
本设计从总体上主要分2个模块,分别是家谱操作模块和文件操作模块。
五.源代码
#define _CRT_SECURE_NO_WARNINGS 1
#pragma warning(disable:6031)
#pragma warning(disable:6011)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <assert.h>
/*字符串最大长度*/
#define STR_LEN 128
/*最大孩子数*/
#define CHILD_LEN 64
/*缓存容量*/
#define BUFFER_SIZE 1024
/*家谱成员信息*/
typedef struct _tMember {
char name[STR_LEN]; /*姓名*/
char wife[STR_LEN]; /*妻子*/
}Member, * pMember;
/*家谱树节点*/
typedef struct _tTreeNode {
Member member; /*成员信息*/
struct _tTreeNode* parent; /*父节点*/
struct _tTreeNode* children[CHILD_LEN]; /*孩子节点*/
int count; /*孩子数量*/
int level; /*节点层级*/
}TreeNode, * pTreeNode;
/*创建树节点*/
pTreeNode createTreeNode(pTreeNode parent, pMember member) {
pTreeNode node = (pTreeNode)calloc(1, sizeof(TreeNode));
if (node) {
node->member = *member;
if (parent) {
node->parent = parent;
node->level = parent->level + 1;
parent->children[parent->count++] = node;
}
}
return node;
}
/*删除树节点*/
void removeTreeNode(pTreeNode cursor) {
if (cursor) {
if (cursor->parent) {
pTreeNode parent = cursor->parent;
int position = -1;
for (int index = 0; index < parent->count; ++index) {
if (parent->children[index] == cursor) {
position = index;
break;
}
}
if (position != -1) {
for (int index = position + 1; index < parent->count; ++index) {
parent->children[index - 1] = parent->children[index];
}
--parent->count;
}
}
}
}
/*计算成员层级*/
int countMemberLevel(const char content[]) {
int level = 0;
const char* cursor = content;
while (*cursor == ' ') {
++level;
++cursor;
}
return level;
}
/*输入性别*/
void inputSex(char sex[]) {
do {
int option;
printf("\n");
printf("----------\n");
printf(" 1 男\n");
printf(" 2 女\n");
printf("----------\n");
printf(" 请选择:");
scanf("%d", &option);
switch (option) {
case 1:
strcpy(sex, "男");
return;
case 2:
strcpy(sex, "女");
return;
}
} while (1);
}
/*检测日期(yyyy-mm-dd)*/
int checkDate(const char date[]) {
int yyyy, mm, dd;
if (strlen(date) != 10) return 0;
if (sscanf(date, "%4d-%2d-%2d", &yyyy, &mm, &dd) != 3) return 0;
if (yyyy < 1999)return 0;
if (yyyy >= 3000) return 0;
if (mm < 1) return 0;
if (mm > 12) return 0;
if (dd < 1) return 0;
if (dd > 31) return 0;
return 1;
}
/*输入日期*/
void inputDate(char date[]) {
do {
scanf("%s", date);
if (checkDate(date)) break;
printf("格式错误,请重新输入!(yyyy-mm-dd)\n");
} while (1);
}
/*编辑家谱成员信息*/
void editFamilyMember(pMember member) {
printf("╔-----------------------------------------╗\n");
printf(" $ 编辑家谱成员信息 $\n");
if (strlen(member->name)) {
printf(" 姓名:%s\n", member->name);
}
else {
printf(" 姓名:");
scanf("%s", member->name);
}
printf(" 妻子:");
scanf("%s", member->wife);
printf("╚-----------------------------------------╝\n");
}
/*输出成员标题*/
void showFamilyMemberTitle() {
printf(" %-10s", "姓名");
printf(" %-10s", "妻子");
printf("\n");
}
/*输出成员信息*/
void showFamilyMember(pMember member, int newline) {
printf(" %-10s", member->name);
printf(" %-10s", member->wife);
if (newline) {
printf("\n");
}
}
/*递归遍历显示家谱信息*/
void recursiveFamilyTreeNodeShow(pTreeNode cursor, int brother_line[], int flag, int all) {
if (cursor) {
char generation[STR_LEN] = { 0 };
if (all) {
showFamilyMember(&cursor->member, 0);
}
sprintf(generation, "【%d世】", cursor->level + 1);
printf("%10s", generation);
if (cursor->level > 0) {
for (int index = 0; index < cursor->level - 1; ++index) {
if (brother_line[index]) {
printf(" │");
}
else {
printf(" ");
}
}
if (flag) {
printf(" ├─>");
}
else {
printf(" └─>");
}
}
printf(" %s %s\n", cursor->member.name, cursor->member.wife);
for (int index = 0; index < cursor->count; ++index) {
int flag = (index < cursor->count - 1);
if (flag) {
brother_line[cursor->level] = 1;
}
else {
brother_line[cursor->level] = 0;
}
pTreeNode child = cursor->children[index];
recursiveFamilyTreeNodeShow(child, brother_line, flag, all);
}
brother_line[cursor->level] = 0;
}
}
/*递归遍历存储家谱信息*/
void recursiveFamilyTreeNodeSave(pTreeNode cursor, FILE* output) {
if (cursor) {
if (cursor->level > 0) {
char format[STR_LEN] = { 0 };
sprintf(format, "%%%ds", cursor->level);
fprintf(output, format, " ");
}
fprintf(output, "%s ", cursor->member.name);
fprintf(output, "%s ", cursor->member.wife);
fprintf(output, "\n");
for (int index = 0; index < cursor->count; ++index) {
pTreeNode child = cursor->children[index];
recursiveFamilyTreeNodeSave(child, output);
}
}
}
/*递归遍历查询家谱信息*/
pTreeNode recursiveFamilyTreeNodeFind(pTreeNode cursor, const char name[]) {
if (cursor) {
if (strcmp(cursor->member.name, name) == 0 || strcmp(cursor->member.wife, name) == 0) {
return cursor;
}
for (int index = 0; index < cursor->count; ++index) {
pTreeNode child = cursor->children[index];
pTreeNode result = recursiveFamilyTreeNodeFind(child, name);
if (result) {
return result;
}
}
}
return NULL;
}
/*递归遍历计算家谱成员数量信息*/
int recursiveFamilyTreeNodeCount(pTreeNode cursor) {
int count = 0;
if (cursor) {
count = 1;
for (int index = 0; index < cursor->count; ++index) {
pTreeNode child = cursor->children[index];
count += recursiveFamilyTreeNodeCount(child);
}
}
return count;
}
/*递归遍历清空家谱信息*/
void recursiveFamilyTreeNodeClear(pTreeNode cursor) {
if (cursor) {
for (int index = 0; index < cursor->count; ++index) {
pTreeNode child = cursor->children[index];
recursiveFamilyTreeNodeClear(child);
}
free(cursor);
}
}
/*从文件中加载家谱信息*/
pTreeNode loadFamilyTree() {
pTreeNode root = NULL;
FILE* input = fopen("familytree.txt", "r");
if (input) {
pTreeNode cursor = NULL;
char buffer[BUFFER_SIZE] = { 0 };
while (fgets(buffer, sizeof(buffer), input)) {
Member member = { 0 };
if (sscanf(buffer, "%s %s", member.name, member.wife) == 2) {
int level = countMemberLevel(buffer);
if (level == 0) {
assert(root == NULL);
root = createTreeNode(NULL, &member);
cursor = root;
}
else {
int step = level - cursor->level;
assert(step <= 1);
if (step != 1) {
int count = 0 - step + 1;
while (count) {
cursor = cursor->parent;
--count;
}
}
cursor = createTreeNode(cursor, &member);
}
}
}
fclose(input);
}
return root;
}
/*将家谱信息存储到文件*/
void saveFamilyTree(pTreeNode root) {
FILE* output = fopen("familytree.txt", "w");
if (output) {
recursiveFamilyTreeNodeSave(root, output);
fclose(output);
}
}
/*显示家谱信息*/
void showFamilyTree(pTreeNode root, int all) {
printf("╔-----------------------------------------╗\n");
printf(" $ 显示家谱信息 $\n");
int* brother_line = (int*)calloc(1024, sizeof(int));
if (all) {
showFamilyMemberTitle();
}
recursiveFamilyTreeNodeShow(root, brother_line, 0, all);
free(brother_line);
printf("╚-----------------------------------------╝\n");
}
/*添加家谱成员*/
void addFamilyTree(pTreeNode* root) {
char name[STR_LEN] = { 0 };
printf("╔-----------------------------------------╗\n");
printf(" $ 添加家谱成员 $\n");
printf(" 输入新成员姓名:");
scanf("%s", name);
if (*root) {
if (!recursiveFamilyTreeNodeFind(*root, name)) {
pTreeNode target = NULL;
char parentname[STR_LEN] = { 0 };
printf(" 输入新成员的父亲或者母亲名字(指定关系):");
scanf("%s", parentname);
target = recursiveFamilyTreeNodeFind(*root, parentname);
if (target) {
Member member = { 0 };
strcpy(member.name, name);
editFamilyMember(&member);
createTreeNode(target, &member);
saveFamilyTree(*root);
showFamilyMemberTitle();
showFamilyMember(&member, 1);
printf("----------------\n");
printf("成功添加以上家谱成员!\n");
}
else {
printf("添加失败,家谱中未找到该名字!\n");
}
}
else {
printf("添加失败,该成员名称已经存在!\n");
}
}
else {
Member member = { 0 };
strcpy(member.name, name);
editFamilyMember(&member);
*root = createTreeNode(NULL, &member);
saveFamilyTree(*root);
showFamilyMemberTitle();
showFamilyMember(&member, 1);
printf("----------------\n");
printf("成功添加以上家谱成员!\n");
}
printf("╚-----------------------------------------╝\n");
}
/*删除家谱成员*/
void removeFamilyTree(pTreeNode* root) {
pTreeNode target = NULL;
char name[STR_LEN] = { 0 };
printf("╔-----------------------------------------╗\n");
printf(" $ 删除家谱成员 $\n");
printf(" 输入姓名:");
scanf("%s", name);
target = recursiveFamilyTreeNodeFind(*root, name);
if (target) {
showFamilyMemberTitle();
showFamilyMember(&target->member, 1);
removeTreeNode(target);
recursiveFamilyTreeNodeClear(target);
if (target == *root) {
*root = NULL;
}
saveFamilyTree(*root);
printf("----------------\n");
printf("成功删除以上家谱成员!\n");
}
else {
printf(" 没有找到相关信息!\n");
}
printf("╚-----------------------------------------╝\n");
}
/*查找家谱成员*/
void findFamilyTree(pTreeNode root) {
pTreeNode target = NULL;
char name[STR_LEN] = { 0 };
printf("╔-----------------------------------------╗\n");
printf(" $ 查找家谱成员 $\n");
printf(" 输入姓名:");
scanf("%s", name);
target = recursiveFamilyTreeNodeFind(root, name);
if (target) {
int* brother_line = (int*)calloc(1024, sizeof(int));
printf("---------------\n");
showFamilyMemberTitle();
showFamilyMember(&target->member, 1);
printf("---------------\n");
printf("【所有孩子】\n");
recursiveFamilyTreeNodeShow(target, brother_line, 0, 0);
free(brother_line);
printf("【所有祖先】\n");
showFamilyMemberTitle();
while (target->parent) {
target = target->parent;
showFamilyMember(&target->member, 1);
}
}
else {
printf(" 没有找到相关信息!\n");
}
printf("╚-----------------------------------------╝\n");
}
/*修改家谱成员*/
void modifyFamilyTree(pTreeNode root) {
pTreeNode target = NULL;
char name[STR_LEN] = { 0 };
printf("╔-----------------------------------------╗\n");
printf(" $ 修改家谱成员 $\n");
printf(" 输入姓名:");
scanf("%s", name);
target = recursiveFamilyTreeNodeFind(root, name);
if (target) {
showFamilyMemberTitle();
showFamilyMember(&target->member, 1);
printf("----------------\n");
editFamilyMember(&target->member);
printf("----------------\n");
saveFamilyTree(root);
printf("成功修改以上家谱成员!\n");
}
else {
printf(" 没有找到相关信息!\n");
}
printf("╚-----------------------------------------╝\n");
}
/*统计家谱成员*/
void statFamilyTree(pTreeNode root) {
int count = recursiveFamilyTreeNodeCount(root);
printf("╔-----------------------------------------╗\n");
printf(" $ 统计家谱成员 $\n");
printf(" 成员数量:%d\n", count);
printf("╚-----------------------------------------╝\n");
}
/*菜单操作*/
void menuOptions(pTreeNode root) {
system("title 家族谱管理系统");
while (1) {
int option = 0;
printf("╔-----------------------------------------╗\n");
printf(" $ 家族谱管理系统 $\n");
printf(" 1 # 显示家谱(精简)\n");
printf(" 2 # 显示家谱(完整)\n");
printf(" 3 # 添加家谱成员\n");
printf(" 4 # 删除家谱成员\n");
printf(" 5 # 查找家谱成员\n");
printf(" 6 # 修改家谱成员\n");
printf(" 7 # 统计家谱成员\n");
printf(" 0 # 退出\n");
printf("╚-----------------------------------------╝\n");
printf(" 请选择:");
scanf("%d", &option);
if (option == 0) break;
switch (option) {
case 1:
showFamilyTree(root, 0);
break;
case 2:
showFamilyTree(root, 1);
break;
case 3:
addFamilyTree(&root);
break;
case 4:
removeFamilyTree(&root);
break;
case 5:
findFamilyTree(root);
break;
case 6:
modifyFamilyTree(root);
break;
case 7:
statFamilyTree(root);
break;
}
}
}
/*主函数*/
int main() {
/*从文件加载家谱信息*/
pTreeNode root = loadFamilyTree();
if (root) {
/*进入操作菜单*/
menuOptions(root);
/*释放家谱树*/
recursiveFamilyTreeNodeClear(root);
}
return 0;
}
六.调试和运行情况简述
本程序包括的两个模块,文件操作和家谱操作功能模块,基本实现了家谱记录输入、读取存盘记录、清除家谱存盘记录、添加成员、存盘、修改家谱成员信息、删除家谱成员实现了查找某人记录、查找某人的孩子、查找某人的祖先、输出家谱等功能。但本系统还有一个缺点,如果文件中没有家谱信息系统将不能正常打开