#include
#include
//链表操作有关的宏
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef struct student
{
char ID[16]; //成员ID号
char name[16]; //成员姓名
char sex[3]; //成员性别
int score[2]; //成员的年龄和电话
char add[16]; //成员地址
char email[16]; //成员e-mail地址
char job[16]; //职业
}ElemType, STUDENT;
//定义链表结点结构
typedef struct lnode
{
ElemType data; //结点数据
struct lnode *next; //指向下一个结点的指针
}LNode, *LinkList, *ListType;
STATUS InitList(LinkList &L );
STATUS DestroyList(LinkList &L );
STATUS ClearList(LinkList L );
STATUS ListEmpty(LinkList L );
STATUS ListLength(LinkList L );
STATUS GetElem(LinkList L, int i, ElemType &e );
int LocateElem(LinkList L, ElemType e,
STATUS (*compare)(ElemType e1, ElemType e2) );
STATUS ListInsert(LinkList L, int i, ElemType e );
STATUS ListDelete(LinkList L, int i, ElemType &e );
STATUS ListTraverse(LinkList L, STATUS (*visit)(ElemType e) );
STATUS ListModify(LinkList L, int i, ElemType e );
void LoadData(LinkList L, char *filename );
void SaveData(LinkList L, char *filename );
//该函数根据用户的选择,完成指定的操作
void UserOperate(ListType L);
/* 用户操作界面启动时,在显示器上显示一些欢迎信息 */
void PrintWelcome( );
/* 在显示器上显示用户可以进行的操作,以及操作的方法 */
void PrintOption( );
void PrintBuy( );
//往线性表中添加一个成员
void AddStu(ListType L);
void DelStu(ListType L);
void ModifyStu(ListType L);
void chkStu(ListType L);
//根据姓名查询信息
void checkname(ListType L);
//根据姓名排序
void Arrangename(ListType L);
//打印
void Prntname(ListType L);
//根据ID排序
void ArrangeID(ListType L);
//打印
void PrntID(ListType L);
//根据职业排序
void ArrangeJob(ListType L);
//打印
void PrntJob(ListType L);
//打印一个人的信息
//调用ListTraverse()函数的时候,将该函数的名字作为第二个参数传递给visit
STATUS PrntOneStu(ElemType stu );
//打印所有人信息
void PrntAll(ListType L);
{
ListType L;
InitList(L );
//从文件中读出数据并插入链表中
LoadData(L, "stuscore.dat" );
//调用用户界面,接受用户的操作选择
UserOperate(L);
//将链表中的数据保存到磁盘文件中
SaveData(L, "stuscore.dat" );
//销毁链表
DestroyList(L );
}
void UserOperate(ListType L)
{
int option = 0;
{
//提示用户操作选择
PrintOption( );
scanf("%d", &option );
switch(option )
{
//添加一个成员
case 1:
AddStu(L);
break;
//删除成员
case 2:
DelStu(L);
break;
//修改各项信息
case 3:
ModifyStu(L);
break;
//根据ID查询各项信息
case 4:
chkStu(L);
break;
//根据姓名查询信息
case 5:
checkname(L);
break;
//打印所有成员信息
case 6:
PrntAll(L);
break;
//按姓名排序:
case 7:
Arrangename(L);
Prntname(L);
break;
//ID排序
case 8:
ArrangeID(L);
PrntID(L);
break;
//职业排序
case 9:ArrangeJob(L);
PrntJob(L);
break;
default: break;
}
}while(option );
PrintBuy( );
}
/* 用户操作界面启动时,在显示器上显示一些欢迎信息 */
void PrintWelcome( )
{
puts(" ");
puts("********************************************************************" );
puts(" 欢迎使用通讯录管理系统!" );
puts("********************************************************************" );
/* 在显示器上显示用户可以进行的操作 */
void PrintOption( )
{
puts(" ");
puts("请选择操作:" );
puts("0:退出系统 " );
puts("1:添加成员 2:删除成员 3:修改成员信息" );
puts("4:根据ID查询成员信息 5:根据姓名查询成员信息 6:打印所有信息 " );
puts("7: 按姓名排序 8:按ID排序 9:按职业排序");
puts(" ");
void PrintBuy( )
{
puts(" 谢谢使用通讯录管理系统,再见!" );
puts("********************************************************************" );
puts(" ");
//若相等则返回TRUE,否则返回FALSE
//当调用线性表操作函数int LocateElem(ListType L, ElemType e, STATUS (*compare)(ElemType e1, ElemType e2)
//的时候,将该函数名作为第三个参数传递给compare
STATUS NameEqual(ElemType e1, ElemType e2 )
{
if( strcmp(e1.name, e2.name)==0 )
return TRUE;
}
STATUS IsEqual(ElemType e1, ElemType e2 )
{
if( strcmp(e1.ID, e2.ID)==0 )
return TRUE;
}
void AddStu(ListType L)
{
ElemType stu;
getchar(); //吃掉前面的回车键,否则后面的字符串输入时会出错
gets(stu.ID );
printf("请输入姓名(不超过15位):" );
gets(stu.name );
printf("请输入年龄:" );
scanf("%d", &stu.score[0] );
printf("请输入电话:" );
scanf("%d", &stu.score[1] );
getchar();
printf("请输入工作:" );
gets(stu.job);
printf("请输入性别:");
gets(stu.sex);
printf("请输入地址:");
gets(stu.add);
printf("请输入email:");
gets(stu.email);
if( LocateElem(L, stu, IsEqual) )
{
puts("操作失败,该成员已经存在" );
return;
}
//为简单起见,我们把新添加的结点放到线性表的前面
if( ListInsert(L, 1, stu ) )
puts("操作成功" );
else
puts("操作失败" );
}
//从线性表中删除一个成员,根据ID决定删除哪个成员
void DelStu(ListType L)
{
ElemType stu;
int pos;
getchar(); //吃掉前面的回车键,否则后面的字符串输入时会出错
gets(stu.ID );
pos = LocateElem(L, stu, IsEqual ); //如果存在该ID的人,则返回其在线性表中的位序,如果不存在返回0
if(pos )
{
if(ListDelete(L, pos, stu) )
{
puts("操作成功");
return;
}
}
}
//修改成员信息,根据ID决定修改哪个
void ModifyStu(ListType L)
{
int pos;
ElemType stu;
getchar(); //吃掉前面的回车键,否则后面的字符串输入时会出错
gets(stu.ID );
printf("请输入姓名(不超过15位):" );
gets(stu.name );
printf("请输入年龄:" );
scanf("%d", &stu.score[0] );
printf("请输入电话:" );
scanf("%d", &stu.score[1] );
getchar();
printf("请输入工作:" );
gets(stu.job);
printf("请输入性别");
gets(stu.sex);
printf("请输入地址");
gets(stu.add);
printf("请输入email");
gets(stu.email);
if(pos > 0 )
{
ListModify(L, pos, stu );
puts("操作成功" );
}
else
{
puts("操作失败" );
}
}
void chkStu(ListType L)
{
int pos;
ElemType stu;
printf("请输入ID号(不超过15位):" );
getchar(); //吃掉前面的回车键,否则后面的字符串输入时会出错
gets(stu.ID );
{
GetElem(L, pos, stu );
PrntOneStu(stu );
}
else
{
puts("操作失败" );
}
//根据姓名查询个人信息
void checkname(ListType L)
{
int pos;
ElemType NAME;
printf("请输入姓名(不超过15位):" );
getchar(); //吃掉前面的回车键,否则后面的字符串输入时会出错
gets(NAME.name );
{
GetElem(L, pos, NAME );
PrntOneStu(NAME );
}
else
{
puts("操作失败" );
}
void Arrangename(ListType L)
{
ListType p,q;
ElemType temp;
p=L->next;
while(p)
{
q=p->next;
while(q)
{
if(strcmp(p->data.name,q->data.name)>0)
{
temp=p->data;
p->data=q->data;
q->data=temp;
}
q=q->next;
}
p=p->next;
}
}
//打印姓名排序信息
void Prntname(ListType L)
{
Arrangename(L);
ListTraverse(L, PrntOneStu );
}
void ArrangeID(ListType L)
{
ListType p,q;
ElemType temp;
p=L->next;
while(p)
{
q=p->next;
while(q)
{
if(strcmp(p->data.ID,q->data.ID)>0)
{
temp=p->data;
p->data=q->data;
q->data=temp;
}
q=q->next;
}
p=p->next;
}
}
//打印ID排序信息
void PrntID(ListType L)
{
ArrangeID(L);
ListTraverse(L, PrntOneStu );
}
//职业排序
void ArrangeJob(ListType L)
{
ListType p,q;
ElemType temp;
p=L->next;
while(p)
{
q=p->next;
while(q)
{
if(strcmp(p->data.job,q->data.job)>0)
{
temp=p->data;
p->data=q->data;
q->data=temp;
}
q=q->next;
}
p=p->next;
}
}
//打印职业排序信息
void PrntJob(ListType L)
{
ArrangeJob(L);
ListTraverse(L, PrntOneStu );
}
//打印一个成员的信息
//调用ListTraverse()函数的时候,将该函数的名字作为第二个参数传递给visit
STATUS PrntOneStu(ElemType stu )
{
printf("%s %s %d %d %s %s %s %s \n", stu.ID, stu.name, stu.score[0], stu.score[1] ,stu.job,stu.sex,stu.add,stu.email);
}
//打印所有成员信息
void PrntAll(ListType L)
{
puts("ID : 姓名 : 年龄: 电话: 工作: 性别: 地址: email地址: ");
ListTraverse(L, PrntOneStu );
STATUS InitList(LinkList &L )
{
//申请一个结点的内存作为头结点
L = (LinkList)malloc(sizeof(LNode) );
if(!L ) exit(OVERFLOW );
return OK;
}
//销毁链表
//释放包括头结点的所有结点
STATUS DestroyList(LinkList &L )
{
LNode *p;
{
p = L;
L = L->next;
free(p );
}
}
//释放头结点之外的所有结点
STATUS ClearList(LinkList L )
{
LNode *p;
{
p = L->next;
L->next = p->next;
free(p );
}
}
//判断链表是否为空,
//返回值,空:TRUE;不空:FALSE
STATUS ListEmpty(LinkList L )
{
return TRUE;
}
int ListLength(LinkList L )
{
int length;
LNode *p;
p = L->next;
while(p )
{
length++;
p=p->next;
}
return length;
}
STATUS GetElem(LinkList L, int i, ElemType &e )
{
int j=1;
LNode *p = L->next;
while(p && j {
p = p->next;
j++;
}
if(!p || j>i )
return FALSE; //找不到位序为i的结点
e = p->data;
return OK;
}
int LocateElem(LinkList L, ElemType e,
STATUS (*compare)(ElemType e1, ElemType e2) )
{
int pos = 1;
LNode *p = L->next;
while(p )
{
if( (*compare)(e, p->data) )
return pos; //元素e在链表中,返回其位置
pos++;
p = p->next;
}
}
//往链表中添加一个结点,结点数据为e
STATUS ListInsert(LinkList L, int i, ElemType e )
{
LNode *p, *s;
int j;
j = 0;
p = L;
while(p && j
p=p->next;
j++;
}
if(!p || j>i-1 )
return ERROR;
s = (LNode*)malloc(sizeof(LNode) );
if(!s ) exit(OVERFLOW );
s->data = e;
s->next = p->next;
p->next = s;
}
STATUS ListDelete(LinkList L, int i, ElemType &e )
{
int j=1;
LNode *p = L;
LNode *q;
while(p->next && j {
p = p->next;
j++;
}
if(!p->next || j>i )
return FALSE; //找不到位序为i的结点
q = p->next;
p->next = q->next;
e = q->data;
free(q );
return OK;
}
STATUS ListTraverse(LinkList L, STATUS (*visit)(ElemType e) )
{
LNode *p;
{
if( !(*visit)(p->data) )
return ERROR;
}
}
STATUS ListModify(LinkList L, int i, ElemType e )
{
LNode *p = L->next;
while(p && j {
p = p->next;
j++;
}
if(!p || j>i )
return FALSE; //找不到位序为i的结点
p->data = e;
return OK;
}
//从文件中将通讯录信息导入内存,每个信息用一个链表节点保存,从文件
//中顺序读通讯录信息的同时,将节点插入链表中
/************************************************************************************
| 息,占的字节数为 | 的信息,占的字节数为 | 的信息,占的字节数为 | …
| sizeof(STUDENT)个 | sizeof(STUDENT)个 | sizeof(STUDENT)个 |
| | |
| | … | … | …
|—-+—-+—-+—–|———————–|———————-|————–
void LoadData(LinkList L, char *filename )
{
STUDENT student; //大家注意STUDENT 数据类型,定义的时候是和ElemType等价的数据类型
FILE *pfile; //文件指针
//打开保存信息的数据文件
pfile = fopen(filename, "rb" );
if(pfile == NULL)
{
printf("文件打开失败!\n" );
exit(0 );
}
ListInsert(L, 1, student ); //放到链表中
pfile = NULL;
}
/************************************************************************************
| 息,占的字节数为 | 的信息,占的字节数为 | 的信息,占的字节数为 | …
| sizeof(STUDENT)个 | sizeof(STUDENT)个 | sizeof(STUDENT)个 |
| | |
| | … | … | …
|—-+—-+—-+—–|———————–|———————-|————–
void SaveData(LinkList L, char *filename )
{
LNode *pnode;
FILE *pfile;
//打开保存信息的数据文件
pfile = fopen(filename, "wb" );
if(pfile == NULL )
{
printf("文件打开失败!\n" );
exit(0 );
}
pnode = L->next;
while(pnode != NULL )
{
fwrite(&(pnode->data), sizeof(STUDENT), 1, pfile ); //每次写入一个人(节点)信息
pnode = pnode->next; //往后移动到下一个节点
lstunum += 1; //统计人数
}
fclose(pfile );
pfile = NULL;
}
指定字符串的替换
电气 161 班 张三 学号:666666
程序分析和设计。
参考手机中的电话薄管理模式,设计一个通讯录查询系统。通过该系统,可以方便查询通中成员的详细信息(ID号,姓名,性别,年龄,职业,电话,住址,E-mail等)。
1、基本要求
实现通讯录的增查改删、和排序。
2、扩展要求(自己加的)
1. 通讯录中所有成员按ID号排序;
2. 通讯录中所有成员按姓名排序;
3. 按职业分类显示所有成员信息;
自己设计一些合理的附加功能
功能分析和模块划分
从功能上说,该系统可以划分为两大功能模块,即数据(电话簿)管理模块和用户操作界面模块(人机交互模块)。
数据管理模块分析
1.该模块的功能是完成所有数据信息的管理,由于成员人数是动态变化的,所以程序中所有的数据采用链表的方式进行组织。程序运行的时候,把所有人的信息放到一个链表中管理。实际上,这个模块的功能是维护一个单向的链表。根据系统要求,应该实现的链表操作函数为:初始化链表、销毁链表,链表插入操作、链表删除操作、定位元素在链表中的位置、修改链表中某个结点的数据、获得链表中某个结点数据、遍历链表等。
2. 根据系统要求和采用的数据结构,设计相关的数据类型如下:
//定义数据对象的类型
typedef struct student
{
char ID[16]; //成员ID号
char name[16]; //成员姓名
char sex[3]; //成员性别
int score[2]; //成员的年龄和电话
char add[16]; //成员地址
char email[16]; //成员e-mail地址
char job[16]; //职业
}ElemType, STUDENT;
//定义链表结点结构
typedef struct lnode
{
ElemType data; //结点数据
struct lnode *next; //指向下一个结点的指针
}LNode, *LinkList, *ListType;
3. 数据文件文件组织
文件名可设计为stuscore.dat,保存到可执行程序的当前目录下。
文件内部数据存储格式如下
存储第一个成员的信息,占字节数为sizeof(STUDENT)个 | 紧跟着存储第二个成员的信息占的字节数为sizeof(STUDENT)个 |
... |
4. 主要函数设计
根据该模块的功能,设计本模块的主要函数如下:
/* 初始化链表 */
STATUS InitList(LinkList &L );
/* 销毁链表,释放包括头结点的所有结点 */
STATUS DestroyList(LinkList &L );
/* 从链表中取位序为i的结点数据,并用e返回该值 */
STATUS GetElem(LinkList L, int i, ElemType &e );
/*判断元素e是否在链表中,如果在返回其在链表中的位序,否则返回0*/
int LocateElem(LinkList L, ElemType e,
STATUS (*compare)(ElemType e1, ElemType e2) );
/*往链表中添加一个结点,结点数据为e*/
STATUS ListInsert(LinkList L, int i, ElemType e );
/*删除位序为i的结点,并用e返回结点数据 */
STATUS ListDelete(LinkList L, int i, ElemType &e );
/*遍历链表中的每个结点,并调用visit()对结点数据处理 */
STATUS ListTraverse(LinkList L, STATUS (*visit)(ElemType e) );
/*修改位序为i的结点数据,将结点数据更新成e的值 */
STATUS ListModify(LinkList L, int i, ElemType e );
/* 从文件中将成员信息导入内存,每个成员信息用一个链表节点保存,从文件中顺序读成员信息的同时,将节点插入链表中 */
void LoadData(LinkList L, char *filename );
/*将链表中的成员信息写到文件中,链表中每个节点都对应一个成员的信息
主函数设计 */
void SaveData(LinkList L, char *filename);
关键代码:
//往链表中添加一个成员
void AddStu();
//从链表中删除一个成员,根据成员的ID决定删除哪个成员
void DelStu();
//修改成员信息,根据ID决定修改哪个成员的信息
void ModifyStu();
//根据ID查询某个成员的信息
void chkStu();
//根据姓名查询个人信息
void checkname();
//姓名排序
void Arrangename()
//ID排序
void ArrangeID();
//职业排序
void ArrangeJob();
//打印所有成员信息
void PrntAll();
这个模块的对外接口函数为:
void UserOperate( )
在这个函数中,根据用户的操作请求,调用对应的函数完成相应的操作。
运行效果
总结
我通过一个程序设计的练习,回顾了这一年里所学和数据结构的知识,也对这些有了更深的了解!同时通过这次实习我也了解都自己在知识掌握上的漏洞和一些实际操作上的不足之处。我也知道了自己今后需要努力的方向!这次实践让我受益非浅,我将在今后的学习中改掉不足之处,努力成为一个优秀的程序设计人员!
程序完全按照要求制作,在排序和查询上设置了更多的功能,希望能得到老师认可。