一、课题任务
人们在日常生活中经常要查找某个人或某个单位的电话号码,本实验将实现一个简单的个人电话号码查询系统,根据用户输入的信息(例如姓名等)进行快速查询
二、设计要求
(1)在外存上,用文件保存电话号码信息;
(2)在内存中,设计数据结构存储电话号码信息;
(3)提供查询功能:根据姓名或编号实现快速查询;
(4)提供其他维护功能,例如插人、删除、修改等。
三、程序的结点设计
现假设链表结点仅含有一个数据域和一个指针域。数据域是为了描述通讯者的相关信息,定义通讯者的结点类型:
typedef struct{
char num[10]; //编号
char name[15]; //姓名
char phone[11]; //电话号码
}dataType;
因此,,线性表的链式存储结构定义如下:
typedef struct node{ //结点类型定义
dataType data; //结点的数据域
struct node *next ; //结点指针域
}listnode , * linklist ;
linklist head ; //定义指向单链表的头指针
listnode * p ; //定义一个指向结点的指针变量
四、程序的功能设计
创建:创建通讯录文件
添加:添加通讯录记录
输出:显示通讯录记录
删除:删除通讯录记录
查找:查询通讯录记录
修改:修改通讯录记录
保存:将信息保存到文件中
五、程序的数据设计
该系统用下面六个功能模块编写,每个模块执行不同的功能,体现了模块化设计的思想。下面六个模块都是利用C语言文件,向文件中追加数据、修改数据、查询数据和删除数据。
建立:可以建立通讯录记录,利用C语言文件,向文件中按顺序输入编号、姓名、电话号码,这里实际上是要求建立一个带头结点的单链表。
添加:可以添加通信录记录,向链表中继续增加结点,但只是输入到内存中,如果要输入到文件中还得调用保存函数。
输出:也是用通过循环依次输出文件中的数据,即输出所有通讯录里的记录。
删除:输入你要删除的人的姓名或编号后,系统会自动删除他的记录,在删除通过switch语句满足提示你确认删除的功能,只有等你确认删除后才会删除。
查询:为了满足用户的实际需求,设计了两种查询方式,可以用姓名、编号两种方式查找通讯录记录。这里用到strcmp()函数,通过比较字符串是否相同来判别是否找到相关信息,找到后,就把文件中的数据赋给对应的变量,再把变量所带的值输出到屏幕上。
修改:当你选择修改功能后,系统会在修改函数中调用查询函数来找到需要修改的记录,然后返回该记录的结点。再通过printf语句来显示他的记录,再通过修改查询函数返回的结点来修改所需要修改的记录。
保存:用到fclose()函数,每次操作成功后会自动保存到指定的文件中。
六、程序的函数设计
主函数和菜单选择函数部分:
int main(){
for( ; ; ){
switch( menu_select() ){
case 1:
printf("\t\t\t建立\n\n");
head = createlist();
break;
case 2:
printf("\t\t\t添加\n\n");
printf("编号 姓名 电话号码\n");
p = ( linklist ) malloc ( sizeof ( listnode ) );
scanf ( "%s%s%s" , p->data.num , p->data.name , p->data.phone ) ;
insertnode(head,p);
break;
case 3:
printf("\t\t\t查询\n\n");
p = listfind( head );
if(p!=NULL){
printf("编号 姓名 电话号码 \n");
printf("%s %s %s\n", p->data.num , p->data.name, p->data.phone );
}
else
printf("没有查询到!\n");
break;
case 4:
modify(head);
printf("修改成功!\n");
printf("请保存!\n");
break;
case 5:
printf("\t\t\t删除\n\n");
delnode(head);
break;
case 6:
printf("\t\t\t输出\n\n");
printlist(head);
break;
case 7:
save(head);
printf("保存成功!\n");
system("pause");
break;
case 0:
printf("\t\t\t亲爱的用户,白白了!我会想你的哦 >3
return 0 ;
}
}
return 0 ;
}
int menu_select(){
int n;
printf("\t\t\t**********************\n");
printf("\t\t\t *********\n");
printf ( "\t\t\t个人电话号码查询系统\n");
printf ("\t\t\t<== 1 建立 ==>\n");
printf ("\t\t\t<== 2 添加 ==>\n");
printf ("\t\t\t<== 3 查询 ==>\n");
printf ("\t\t\t<== 4 修改 ==>\n");
printf ("\t\t\t<== 5 删除 ==>\n");
printf ("\t\t\t<== 6 输出 ==>\n");
printf ("\t\t\t<== 7 保存 ==>\n");
printf ("\t\t\t<== 0 退出 ==>\n");
printf ("\t\t\t<== 请选择 ==>\n");
printf("\t\t\t *********\n");
printf("\t\t\t**********************\n:");
for(; ;){
scanf ( "%d" , &n );
if( n < 0 || n > 7 )
printf ("输入错误,请重选:");
else break;
}
return n;
}
上面主要是主函数和菜单选择函数程序部分,这部分是通过一个无限循环来执行上述所有的功能模块,(用for和switch语句解决),如果用户要退出的话,只需选择“0”就可以成功退出并且自动将数据保存到指定的文件中。输入变量为n,它作为menu_selet函数的返回值提供给switch语句。使用for循环实现重复选择,并在主函数中实现。实际使用时,只有选择0—7才会有用,对于不符合要求的输入,提示输入错并要求重新输入。
创建模块:
1..打开存储的文件,若成功打开,则读取已有信息。
2.若打开存储的文件失败,则自己建立一个文件,并且开始输入相应的信息。建表表完成判断是继续还是离开。
3.打开存储的文件,若成功打开则,则读取已有信息。
4.若打开存储的文件失败,则自己建立一个文件,并且开始输入相应的信息。建表表完成判断是继续还是离开。
linklist createlist()
{
linklist createlist(){ //建立
if( ( fp = fopen(dir_name,"r") ) != NULL) { // 打开文件
linklist head = ( linklist ) malloc ( sizeof ( listnode ) ) ;
//动态建立空间
listnode * p ,* rear;
rear = head ;
char a[30] , b[30] , c[30];
fscanf( fp ,"%s%s%s\n",a,b,c); //读取"编号 姓名 电话号码" 这几个汉字
while(!feof(fp)){ // 检测 文件是否结束 不是则继续循环,是则退出
p = ( linklist ) malloc ( sizeof ( listnode ) );
fscanf ( fp ,"%s%s%s\n",p->data .num ,p->data .name ,p->data .phone ); //读入信息进文档
rear->next =p;
rear=p;
}
rear->next =NULL;
fclose(fp);
printf("成功读取文件记录!\n");
return head;
}
else{
printf("不存在记录文件,请自己输入:\n") ;
linklist head=( linklist ) malloc ( sizeof ( listnode ) );//申请新结//点
listnode *p,*rear ;
int flag=0; // 定义一个变量来判断是否循环
rear=head;//尾指针初始指向头结点
while(flag==0) {
p =( linklist ) malloc ( sizeof ( listnode ) );
printf("编号 姓名 电话号码\n");
scanf ( "%s%s%s" , p->data.num , p->data.name , p->data.phone ) ;
rear->next = p; // 新结点连结到尾结点之后
rear = p; //尾指针指向新结点
printf("按1结束建表,按0继续建表。\n");
printf("结束建表吗?(1/0):");
scanf ( "%d" , &flag ); //读入一个标志数据
}
rear->next =NULL; //终端结点指针域置空
return head; //返回链表头指
}
}
在这个函数里,首先看文件是否存在,如果存在,就把信息从外存调到内存中来,如果不存在,就会提示不存在记录文件,请自己输入。要建立链表,首先要生成结点,在这里用的是尾插法建立链表,这里没有用排序功能,自己输入信息时得按编码从小到大输入。
添加模块:
void insertnode(linklist head,listnode *p){ // 添加函数
listnode *p1,*p2;
p1=head;
p2=p1->next ;
while(p2 != NULL && strcmp( p2->data .num ,p->data .num ) < 0) {
// 排序插入, 找到不比插入的数据小的节点。
p1=p2;
p2=p2->next ;
}
p1->next =p;
p->next =p2;}
把要添加的信息输入到一个结点当中,然后通过排序插入,找到不比插入的数据小的结点,然后插在此结点的前面。
输出模块:
void printlist(linklist head){ //输出
listnode *p;
p = head->next ;
if( p == NULL)
printf("该系统中目前还没有联系人!\n\n");
printf("编号 姓名 电话号码\n");
while( p != NULL ){
printf("%s %s %s\n",p->data .num ,p->data .name ,p->data .phone );
p=p->next ;
}
}
该函数是通过一个循环反复的从链表中读取个人记录赋给相对应的变量,然后将变量所带的值输出到屏幕上,直到链表为空。如果链表为空就会提示你该系统中目前还没有联系人!
删除模块和查找模块:
1.查找要删除的人,如果没找的进行第2步,否则进行第3步。
2.没找到要找的人,则返回。
3.判断是否删除,如果是,删除信息并返回;否则返回
listnode *listfind(linklist head){ // 查找函数
listnode *p;
char num[5];
char name[9];
int n;
printf("1.按编号查询\n");
printf("2.按姓名查询\n");
printf("请选择:\n");
p = head->next ;
scanf ( "%d" , &n ) ;
if( n == 1 ){
printf("请输入要查找者的编号:");
scanf ( "%s" , num );
while( p && strcmp( p->data.num , num ) < 0) p = p->next ;
if( p == NULL||strcmp(p->data .num ,num)>0) p=NULL;
}
else if ( n == 2 ){
printf( "请输入要查找者的姓名:");
scanf ( "%s" , name ) ;
while( p && strcmp(p->data .name ,name)!=0) p=p->next ;
}
return p;
}
void delnode(linklist head){ //删除
char jx;
listnode *p,*q;
p = listfind( head ) ;
if(p == NULL){
printf("没有查到要删除的人!\n");
return;
}
printf("真的要删除该人的记录吗?(y/n):");
scanf ( "%s" , &jx );
if(jx =='y'||jx =='Y'){
q = head;
while( q != NULL && q->next !=p) q = q->next ;
q->next = p->next ;
free ( p );
printf("已删除!\n");
}
}
用户单选择查找功能,系统会提示你是用编码查找还是用姓名查找。然后通过循环从链表中寻找此人,过程是这样的,每次从链表中读取数据后,便把数据赋给相应的变量,再用该数据与用户输入字符串比较,如果相同,那就说明已找到该人的数据;如果不同,那就执行下一个循环,直到找出为止。系统会显示出该人的所有记录。如果找不到的话,系统会输出:“没有查询到!”并且返回主菜单。如果选择删除功能,会先调用查找函数找到要查询的结点,然后再删除该结点。如果没有查到系统会提示没有要删除的人!
void modify(linklist head){//修改
listnode *p;
p=listfind(head);
printf("\n该人的记录为:\n");
printf("%s %s %s\n",p->data .num ,p->data .name ,p->data .phone );
printf("请修改:\n");
scanf ( "%s%s%s" , p->data.num , p->data.name , p->data.phone ) ;
这个和删除模块有点相似,用户使用这个功能的时候,会先调用查询函数,系统会通过循环从链表中寻找此人,该过程和删除模块是一样的,找到后,系统会显示该人的所有记录,这时用户要按秩序输入数据。用户输入完成后,系统会显示“已修改!”并且返回主菜。
保存模块:
void save(linklist head){//保存
linklist p;
p = head->next ;
if((fp=fopen(dir_name,"w"))==NULL){
printf("保存失败!");
return;
}
fprintf(fp,"%s %s %s\n","编号","姓名","电话号码");
while( p ) {
fprintf(fp,"%s %s %s\n",p->data .num,p->data .name ,p->data .phone );
p=p->next ;
}
fclose(fp);
}
该程序中要用到文件操作,要将内存中的信息存到记事本中,就要调用保存函数。
七、总结
该课题是设计一个电话号码查询系统,该电话号码查询系统能够很好的管理好我们的通讯录记录,也拥有比较完善的功能,但是该系统依旧有漏洞,此程序必须按照系统提示的操作来执行,不然的话就会使程序进入死循环!并且后面的删除模块所用的方法是用删除结点来删除记录,虽然可以删除,但达不到理想的效果。
在整个系统程序设计的过程中,我可以感受到自己对数据结构有了一定的掌握!但也有着许多不足之处。特别是在程序的逻辑部分还存在着严重的不足。我逐步的了解了程序模块化设计的思想含义。在整个程序设计的过程中语法的错误还是比较容易检查的,但是对于那些逻辑思维的问题方面的缺陷不易查出,因此常常出现系统提示没有错误但是仍然不能够正常运行,不能够达到理想的状况,即函数在逻辑方面存在错误!
此外编程是一项高精度的工作,所以我们要有规范化,标准化的代码编写习惯,通过这次编程我们深深的感受到对代码的变量命名,代码内注释格式,甚至函数都有明确规定,良好的编写习惯,不但有助于代码的纠错,也有助于不同人员之间的协作。
我们还要有模块化思维能力,模块化思维就是编程任何一个功能模块或函数的时候,要多想一些,不要局限在完成当前任务的简单思路上,想想看该模块是否可以脱离这个系统存在,是否可以通过简单的修改参数的方式在其他系统和应用环境下直接引用,这样就能极大的避免重复性的工作。
本文来源:https://www.2haoxitong.net/k/doc/d5b533a2804d2b160a4ec09e.html
文档为doc格式