前言:最近在复习完C语言后,突然想做个小东西巩固巩固学习的知识,所以写了这个小系统,权当练手。如果对大家有帮助,万分荣幸。
一、 系统综述:
本系统是一个小型的学生管理系统(C语言实现)。从功能上说,实现了简单的增删查改等功能。具体上说:
(1)、增加一条学生信息:需要按提示输入相应的学生记录属性。
(2)、删除一条学生信息:按照学号删除对应的学生记录,所以需要由用户输入待删除的学号。
(3)、查询一条学生信息:按照学号查询对应的学生记录,所以需要由用户输入待查询的学号。
(4)、修改一条学生信息:按照记录修改对应学生记录的各项属性。
(5)、查看所有的学生信息:查看所有的学生记录信息。
(6)、删除所有的学生信息:删除文件中所有的学生的信息。
(7)、显示目前系统中的学生人数及学号:即在文件系统中存储的学生记录人数及所有学生的学号。
(8)、退出系统。
二、 基本的实现过程概况:
本系统保存两个数据文件:一个是student.dat,一个是student_num.dat。前者是存储基本的记录数据(学号、姓名、性别、年龄、年级、班级)(二进制存储)。后者只存储目前文件中学生的人数(二进制存储)。
在每次开始运行系统的时候,程序从student_num中读取当前文件中存储的学生人数之内存中(变量stus.nums中),从student.dat读取所有学生的学号之内存中(变量stus.stu_num[])。然后所有的功能的实现基本上都需要这两个变量(封装成了Students结构体)。比如说,增加一条记录操作。在输入学生学号的时候,首先会和stus.stu_num[]中已经存储的学号进行比较,如果学号已经存在,则提示用户输入错误。同样的,其他的功能实现也会用到这两个变量。所以,在本系统的实现过程中,为了加快程序运行,减少IO消耗,在程序的一开始就会把这两个变量加载到内存中,然后常驻内存,知道程序退出。
还有,对于用户选择的功能(增删查改),本程序都是立即反映到文件中。这是什么意思呢?就是说,对于涉及到的文件操作,比如说增加一条记录,当用户输入完一条记录后,程序会立即将记录内容保存至文件中。再比如说,删除一条记录,当确定了用户需要删除的记录之后,程序会立即将文件中的记录删除(即像数组删除一个元素那样不断向前覆盖),当然这样会有很大的IO消耗,关于这点在后面会作介绍。
三、 文件结构及函数调用说明:
一般来说,文件名里存放着对应的函数。其中:
main.c里是main():主要是整个系统初始化及各个模块的总体调用。
add.c里有AddRecord():其功能是增加一条记录。
delete.c里是DeleteOneRecord()和DeleteAllRecord():其中前者是删除文件中的一条记录,后者是删除文件中的所有记录内容。
modify.c里是ModifyRecord():这是修改一条学生的记录。
Show.c里面有ShowOneRecord()、ShowCurStuNums()和ShowAllRecords():对于这三个函数,第一个是查询一条学生记录;第二个是显示当前的学生人数及所有学生的学号;第三个是显示所有学生的记录信息。
exit.c里面有Exit():主要处理退出系统时的处理工作。
meau.c文件中是PrintfMeau()和Choose():前者是打印供用户选择的菜单;后者是具体接受用户选择的函数。
file_ope.c中有File_ReadRrcordNums()、File_ReadStuNums()、File_Init()、File_Add()、File_ReadOneRecord()、File_ReadAllRecords()、File_ModifyRecord()、File_DeleteOneRecord()、File_DeleteAllRecord()、File_Exit():主要是一些文件操作函数分别对应这上述增删查改功能及系统的初始化和退出在文件中的具体实现。
other.c里面有StuNumIsExist()、SearStuPos()、ShowStuInfo()、ShowBeginLine()、ShowEndLine()、ClearWin()、IsEsc()等函数:这里主要是一些辅助函数,包括检测学号是否存在、查询学号的位置、用于显示查询结果的一些信息,清除屏幕内容的信息、判断是否取消等等。
mian.h里面主要是一些结构体定义以及函数声明,目的是为了方便不同文件之间的函数调用。
主函数模块调用流程图:
四、代码及相应说明
/*
名称:main函数
说明:在main函数中,是进行整个管理系统的函数调用。
1.首先是进行初始化相关文件(File_Init())
2.打印菜单。
3.然后用用户选择,执行不同的模块。
4.跳到第二步或退出系统。
*/
#include<stdio.h>
#include "mian.h"
Students stus; //定义全局变量
int main()
{
int opt = 0,exit_flag = 0,clc_flag = 0; //用户选择
if(File_Init() == false) //初始化相关数据文件
{
printf("文件初始化失败\n");
return -1;
}
printf("-------------------------------学生信息管理系统------------------------------- \n");
printf("学生信息的数量为:%d\n",stus.nums);
//用户输入
while(0 == exit_flag )
{
PrintfMeau(); //打印菜单
if(Choose(&opt) == true)
{
switch(opt)
{
case 1:
AddRecord(); //添加一条记录
ClearWin(0);
break;
case 2:
DeleteOneRecord(&clc_flag); //删除一条记录
ClearWin(clc_flag);
break;
case 3:
ShowOneRecord(&clc_flag); //查询一条记录
ClearWin(clc_flag);
break;
case 4:
ModifyRecord(); //修改一条记录
ClearWin(0);
break;
case 5:
ShowAllRecords(); //显示所有记录
ClearWin(0);
break;
case 6:
DeleteAllRecord(); //删除所有记录
ClearWin(0);
break;
case 7:
ShowCurStuNums(); //显示系统中的学生数量
ClearWin(0);
break;
case 8:
Exit(&exit_flag); //退出系统
break;
}
}
}
return 0;
}
/*
名称:添加一条学生记录
说明:AddRecord():首先由用户输入记录中的相关属性。待全部输入成功后,用File_add函数写入到文件中。
*/
#include<stdio.h>
#include "mian.h"
//添加一条学生记录
bool AddRecord()
{
Student add_stu;
int val = 0;
char c = 0;
//学生编号
while(true)
{
printf("请输入新增加的学生编号(20位):");
scanf("%d",&val);
if(StuNumIsExist(val) == true) //判断学号是已经存在
printf("学号已经存在了哦,换个学号吧!\n");
else
break;
}
add_stu.stu_num = val;
//姓名
printf("请输入新增加的学生姓名(20个字符):");
scanf("%s",&add_stu.name);
//年龄
while(true)
{
printf("请输入新增加学生的年龄(5-40):");
scanf("%d",&val);
if(val>=5 && val <= 40)
break;
else
printf("学生的年龄应该在5-40岁之间哦!\n");
}
add_stu.age = val;
//性别
while(true)
{
printf("请输入新增加的学生性别(M\\m代表男,W\\w代表女):");
c = getchar();
scanf("%c",&c);
if(c == 'M' || c == 'm')
{
add_stu.sex = 'M';
break;
}
else if(c == 'W' || c == 'w')
{
add_stu.sex = 'W';
break;
}
else if(!isspace(c)) //--->isspace()是标准库中用来判断是不是空白字符的函数<---
{
printf("我去,这个学生是男的还是女的啊!\n");
}
}
//年级
while(true)
{
printf("请输入新增加学生的年级(0-20):");
scanf("%d",&val);
if(val>=0 && val <= 20)
break;
else
printf("这个学生这么叼,不符合常理吧!\n");
}
add_stu.grade = val;
//班级
printf("请输入新增加学生的班级:");
scanf("%d",&val);
add_stu._class = val;
//写入文件
if(File_Add(&add_stu) == false)
{
printf("写入文件出错!\n");
return false;
}
else
{
stus.stu_nums[stus.nums++] = add_stu.stu_num; //增加一条记录
printf("成功写入一条学生记录。\n");
ShowBeginLine();
ShowStuInfo(&add_stu);
ShowEndLine();
return true;
}
}
/*
名称:删除一条学生记录和删除所有记录
说明:
1.DeleteOneRecord(int *clc_flag):是删除一条学生记录。基本思路是由用户
输入待删除的学号(输入失败可以重新输入或取消本次输入)。然后在stus.stu_num
[]中查找记录在文件中的位置,最后调用File_DeleteOneRecord(index)在文件中删除
此条记录。
还有一点要说明的是,函数的参数clc_flag是用来判断采用哪种方式来清空
屏幕内容,即增加了取消功能后导致在每次清空屏幕时,有两种不同的方式清空方式。
一种是按下两次回车键(或者其他键),一种是按下一次回车键即可。为了方便用户使用
,所以用了一个标志位来区分。不清楚的可以把clc_flag值都赋为0,运行程序实际体验
一下。
2.DeleteAllRecord():这是删除所有的学生记录。基本的思路是用只写的方式打开文件,
这样的话会自动清空文件内容,执行成功后,然后关闭文件即可。
*/
#include<stdio.h>
#include "mian.h"
//删除一个记录
bool DeleteOneRecord(int *clc_flag)
{
*clc_flag = 0;
char c = 0;
int stu_num = 0,index = -1;
//输入学号
while(true)
{
printf("这位同学,请输入你要删除的学生的编号:");
scanf("%d",&stu_num);
if((index = SearStuPos(stu_num)) == -1)
{
printf("这位同学,不好意思,你查找的学生不存在!\n");
printf("请重新输入或者取消本次操作(按Esc键取消,其他键重新输入)...");
c = getchar();
c = getch();
if(IsEsc(c) == true)
{
printf("\n哎呀,取消本次操作了哦!\n");
*clc_flag = 1;
return false;
}
else
printf("\n");
}
else
break;
}
//获取学生学号在文件中的位置
//index = SearStuPos(stu_num);
if(File_DeleteOneRecord(index) == false)
{
printf("文件删除失败!\n");
*clc_flag = -1;
return false;
}
else
{
printf("成功删除一条记录。\n");
--stus.nums; //学生的数量减1
*clc_flag = 0;
}
return true;
}
//删除所有记录
bool DeleteAllRecord()
{
if(File_DeleteAllRecord() == false)
{
printf("删除所有记录失败!\n");
return false;
}
else
{
printf("成功删除所有记录!\n");
return true;
}
}
/*
名称:退出函数
说明:Exit(int *flag):在这里面是调用了File_Exit(),主要作用是在用户选择退出时,将stus.nums重新
写入文件student_num中。以便下次运行程序时,重新从student_num中读取文件存储的学生记录数目。
*/
#include<stdio.h>
#include "mian.h"
//退出系统
bool Exit(int *flag)
{
*flag = 1;
if(File_Exit() == false)
{
printf("退出时,保存失败!\n");
return false;
}
else
{
printf("ok,成功退出系统\n");
return true;
}
}
/*
名称:各种文件操作
说明:这里面涉及到的都是具体的文件操作。供其他函数调用。包括:
1. File_ReadRrcordNums():程序运行时从student_num.dat读取现存的
记录数目stus.nums。
2.File_ReadStuNums():在用户具体选择之前,从student.dat中读取所有
学生的学号,待以后查询使用。
3.File_Init():文件的初始化。主要是在文件不存在时创建新文件。文件存
在时,调用上面两个函数,从文件中读取相关内容至stus中。
4.File_Add(Student *stu):这个函数接收一个Student类型的参数。在函数
的内部进行文件操作,将接收来的参数写入到文件中。这里要注意的是:增添
的记录并不一定是写入到文件的末尾,而是写入到stus.num*sizeof(Student)
的位置。这是因为在删除一条记录的时候,是向前覆盖式删除,所以最后一条
记录并没有被实际的删除,只是stus.num少了一个,感觉是删除了。所以重新
增添写入的时候,要写入到stus.num*sizeof(Student)位置。
5. File_ReadOneRecord(Student *stu,int index):这个函数从指定的位置读
取一条记录。基本的过程是首先用fseek定位到指定位置,然后用fread()读取一
个Student的数据。
6.File_ReadAllRecords(Student * temp_stu):文件操作,从文件中把所有的记
录读到temp_stu[]中,最后显示出来。
7.File_ModifyRecord(int index,Student *mod_stu):修改记录基本的操作是从
文件中读出指定位置的记录,然后由用户修改后,在此写入到文件中。
8.File_DeleteOneRecord(int index):删除一条记录。即从待删除的位置的后一个
位置不断向前覆盖,以达到删除的目的。(文件末尾最后一个位置不删除)
9.File_DeleteAllRecord():删除所有的记录。即以写入的方式打开(库函数会自动)
清空文件。然后在关闭文件即可。
10.File_Exit():退出系统时调用的函数。即把stus.nums写入student_num.dat文件。
以待以后读取。
*/
#include<stdio.h>
#include "mian.h"
//读取学生记录的数量
bool File_ReadRrcordNums()
{
FILE *fp = NULL;
if(((fp = fopen("student_num.dat","rb+")) == NULL))
{
printf("打开文件失败!\n");
return false;
}
//读取信息数量(1个int型数据)
if(fread(&stus.nums,sizeof(int),1,fp) != 1)
{
printf("读取学生信息数量失败!\n");
return false;
}
fclose(fp);
return true;
}
//读取所有学生的编号
bool File_ReadStuNums()
{
int i = 0,ret_val = 1;
FILE *fp = NULL;
if(File_ReadRrcordNums() == false)
{
printf("读取学生编号失败!\n");
return false;
}
if(((fp = fopen("student.dat","rb+")) == NULL))
{
printf("打开文件失败!\n");
return false;
}
while(i < stus.nums)
{
fseek(fp,i*sizeof(Student),SEEK_SET); //移动到不同的位置
ret_val = fread(&stus.stu_nums[i],sizeof(int),1,fp); //按顺序读取学生学号
++i;
}
fclose(fp);
return true;
}
//文件初始化
bool File_Init()
{
FILE *fp;
//判断学生信息文件
if(((fp = fopen("student.dat","rb+")) == NULL)) //文件不存在
{
printf("数据文件不存在,新建文件student.dat\n");
if((((fp = fopen("student.dat","wb+")) == NULL)))
{
printf("新建文件失败!\n");
return false;
}
//判断学生信息数量文件
if(((fp = fopen("student_num.dat","rb+")) == NULL))
{
printf("数据文件不存在,新建文件student_num.dat\n");
if((((fp = fopen("student_num.dat","wb+")) == NULL)))
{
printf("新建文件失败!\n");
return false;
}
stus.nums = 0; //初始化时,学生信息为0
}
}
else //文件存在
{
if(File_ReadRrcordNums() == false) //读取文件中学生记录的数量
{
printf("读取学生记录失败!\n");
return false;
}
if(File_ReadStuNums() == false) //读取文件中学生的学号
{
printf("读取学生学号失败!\n");
return false;
};
}
return true;
}
//文件操作:增加一条文件记录
bool File_Add(Student *stu)
{
int ret_val = 0;
long add_pos = 0;
FILE *fp;
if(((fp = fopen("student.dat","rb+")) == NULL))
{
printf("打开文件失败!\n");
return false;
}
add_pos = sizeof(Student) * stus.nums;
fseek(fp,add_pos,SEEK_SET); //移到待添加的记录位置
ret_val = fwrite(stu,sizeof(Student),1,fp); //写入一组数据
if(ret_val != 1)
return false;
fclose(fp);
return true;
}
//读取指定的一条学生记录
bool File_ReadOneRecord(Student *stu,int index)
{
int ret_val = 1;
FILE *fp = NULL;
if(((fp = fopen("student.dat","rb+")) == NULL))
{
printf("打开文件失败!\n");
return false;
}
fseek(fp,index*sizeof(Student),SEEK_SET); //每次移动到不同的位置
ret_val = fread(stu,sizeof(Student),1,fp); //按顺序读取学生学号
if(ret_val != 1)
return false;
fclose(fp);
//ShowStuInfo(&temp_stu);
return true;
}
//读取文件中所有的学生信息
bool File_ReadAllRecords(Student * temp_stu)
{
int i = 0;
// Student temp_stu;
FILE *fp = NULL;
if(((fp = fopen("student.dat","rb+")) == NULL))
{
printf("打开文件失败!\n");
return false;
}
do
{
fseek(fp,i*sizeof(Student),SEEK_SET); //每次移动到不同的位置
fread(&temp_stu[i],sizeof(Student),1,fp); //按顺序读取学生学号
++i;
}while(i < stus.nums);
fclose(fp);
return true;
}
//修改一个学生的信息
bool File_ModifyRecord(int index,Student *mod_stu)
{
int ret_val = 0;
FILE *fp = NULL;
if(((fp = fopen("student.dat","rb+")) == NULL))
{
printf("打开文件失败!\n");
return false;
}
fseek(fp,index*sizeof(Student),SEEK_SET); //移动到需要修改的位置
ret_val = fwrite(mod_stu,sizeof(Student),1,fp); //写入修改的一组数据
if(ret_val != 1)
return false;
fclose(fp);
return true;
}
//删除一条记录
bool File_DeleteOneRecord(int index)
{
int ret_val = 1,i = 0;
Student temp_stu;
FILE *fp = NULL;
if(((fp = fopen("student.dat","rb+")) == NULL))
{
printf("打开文件失败!\n");
return false;
}
//fseek(fp,index*sizeof(Student),SEEK_SET); //每次移动到不同的位置
//移动记录
for(i = index+1;i<stus.nums;++i)
{
fseek(fp,i*sizeof(Student),SEEK_SET); //移动到待删除指定位置的后一个位置
fread(&temp_stu,sizeof(Student),1,fp); //从指定位置读取一条记录
fseek(fp,(i-1)*sizeof(Student),SEEK_SET); //向前移动一个指针位置
fwrite(&temp_stu,sizeof(Student),1,fp); //重新写入到移动后的位置
stus.stu_nums[i-1] = stus.stu_nums[i]; //学号也向前移动
}
fclose(fp);
return true;
}
//文件操作:删除所有记录
bool File_DeleteAllRecord()
{
FILE *fp = NULL;
if(((fp = fopen("student.dat","wb+")) == NULL))
{
printf("打开文件失败!\n");
return false;
}
fclose(fp);
stus.nums = 0; //学生信息数量重新置零
return true;
}
//退出时,将学生记录的数量写入student_num.dat文件
bool File_Exit()
{
FILE *fp = NULL;
if(((fp = fopen("student_num.dat","rb+")) == NULL))
{
printf("打开学生数量文件失败!\n");
return false;
}
if(fwrite(&stus.nums,sizeof(int),1,fp) != 1)
{
printf("写入学生数量文件失败!\n");
return false;
}
fclose(fp);
return true;
}
/*
名称:打印菜单及用户选择
说明:
1.PrintfMeau():每次循环,打印菜单,供用户选择。
2.Choose(int *opt):用户选择函数。
*/
#include<stdio.h>
#include "mian.h"
//打印菜单
void PrintfMeau()
{
printf("-----------------------------------选择菜单----------------------------------- \n");
printf("\t<1> 增加一条学生信息 ");
printf("\t<2> 删除一条学生信息 \n");
printf("\t<3> 查询一条学生信息 ");
printf("\t<4> 修改一条学生信息 \n");
printf("\t<5> 查看所有的学生信息 ");
printf("\t<6> 删除所有的学生信息 \n");
printf("\t<7> 显示目前系统中的学生人数及学号");
printf("\t<8> 退出系统 \n");
printf("\n\n");
printf("-----------------------------------选择菜单-----------------------------------\n");
printf("亲,请选择您想要的操作:");
}
//用户选择
bool Choose(int *opt)
{
scanf("%d",opt);
if(*opt <= 0 || *opt >= 9 )
{
printf("不好意思,输入有误,请重新输入\n");
return false;
}
else
return true;
}
/*
名称:修改学生信息
说明:ModifyRecord():修改一条记录。需要有用户输入指定的学号。此函数基本的
流程是当用户输入完待修改的学号后,程序从文件中读取指定的记录,然后提示用
户进行修改。当修改完毕之后,在写回文件指定位置。
*/
#include<stdio.h>
#include "mian.h"
//修改学生信息
bool ModifyRecord()
{
int stu_num = 0,index = -1,val = 0;
char c = 0;
Student mod_stu;
//输入学号
while(true)
{
printf("这位同学,请输入你要修改的学生的编号:");
scanf("%d",&stu_num);
if((index = SearStuPos(stu_num)) == -1)
printf("这位同学,不好意思,你查找的学生不存在请重新输入!\n");
else
break;
}
//获取学生学号在文件中的位置
//index = SearStuPos(stu_num);
if(File_ReadOneRecord(&mod_stu,index) == false)
{
printf("读取一条记录出错!\n");
return false;
}
printf("目前要修改的学生的名字为:%s\n",mod_stu.name);
printf("你想修改成的名字为:");
scanf("%s",&mod_stu.name);
printf("目前要修改的学生的年龄为:%d\n",mod_stu.age);
//年龄
while(true)
{
printf("请输入修改后学生的年龄(5-40):");
scanf("%d",&val);
if(val>=5 && val <= 40)
break;
else
printf("学生的年龄应该在5-40岁之间哦!\n");
}
mod_stu.age = val;
printf("目前要修改的学生的性别为:%c\n",mod_stu.sex);
//性别
while(true)
{
printf("请输入要修改的的学生性别(M\\m代表男,W\\w代表女):");
c = getchar();
scanf("%c",&c);
if(c == 'M' || c == 'm')
{
mod_stu.sex = 'M';
break;
}
else if(c == 'W' || c == 'w')
{
mod_stu.sex = 'W';
break;
}
else if(!isspace(c)) //--->isspace()是标准库中用来判断是不是空白字符的函数<---
{
printf("我去,这个学生是男的还是女的啊!\n");
}
}
printf("目前要修改的学生的年级为:%d\n",mod_stu.grade);
//年级
while(true)
{
printf("请输入要修改学生的年级(0-20):");
scanf("%d",&val);
if(val>=0 && val <= 20)
break;
else
printf("这个学生这么叼,不符合常理吧!\n");
}
mod_stu.grade = val;
printf("目前要修改的学生的班级为:%d\n",mod_stu._class);
//班级
printf("请输入要修改学生的班级:");
scanf("%d",&val);
mod_stu._class = val;
if(File_ModifyRecord(index,&mod_stu) == false) //将修改保存至文件中
{
printf("修改学生信息出错!\n");
return false;
}
else
{
printf("成功修改学生信息.\n");
return true;
}
}
/*
名称:一些辅助函数
说明:这里是一些辅助的函数。包括
1.StuNumIsExist(int stu_num):判断学号是否存在。由于在内存中保存着文件系统中所有的学生记录,那么
当用户进行查询或者删除(一切有关学生学号的操作),即可通过首先在内存中查询待处理的学生记录是否存
在,避免了重复的IO消耗。
2.SearStuPos(int stu_num):查询待处理的记录在文件中的位置。此函数基本上和上面判断学号是否存在的函数
基本上差不多。为了程序的更清晰,就写了两个函数。从某种程度上说 ,在内存中的所有学生记录算是一种索引,
因为在内存中的记录位置和文件中对应记录的位置是一致的,通过查询内存中学号的位置,来查找文件中对应记录
的位置。
3.ShowStuInfo(Student *stu):显示学生的信息。即显示学生记录的各项属性。
4. ShowBeginLine():输出显示的开头内容。
5.ShowEndLine():输出显示的结尾线
以上三个函数是为了保证输出结果更美观而设计的。
6. ClearWin(int clc_flag):清空屏幕。为了保持无关的干扰输出,每次执行完用户选择的功能后,会调用这个函数
清空屏幕。为了更人性化,当用户在删除或者查询时第一次输入错误的学号后,会给用户选择退出本次
操作的机会。(退出本次操作,不是退出系统)
*/
#include<stdio.h>
#include "mian.h"
//检查学号是否已经存在
bool StuNumIsExist(int stu_num)
{
int i = 0;
for(i = 0;i<stus.nums;++i)
{
if(stu_num == stus.stu_nums[i])
return true;
}
return false;
}
//查找的学生的位置
int SearStuPos(int stu_num)
{
int i = 0;
for(i = 0;i<stus.nums;++i)
{
if(stu_num == stus.stu_nums[i])
{
return i;
}
}
return -1;
}
//显示学生信息
void ShowStuInfo(Student *stu) ///---->???常量指针和指针常量???<----
{
printf(" %-8d ", stu->stu_num);
printf("%-20s", stu->name);
printf("%-6c", stu->sex);
printf("%4d", stu->age);
printf("%18d", stu->grade);
printf("%10d\n", stu->_class);
return ;
}
//输出显示的开头内容
void ShowBeginLine()
{
printf("执行结果为:\n");
printf("-----------------------------------------------------------------------------\n");
printf(" 学号 姓名 性别 年龄 年级 班级\n");
printf("------------------------------------------------------------------------------\n");
}
//输出显示的结尾线
void ShowEndLine()
{
printf("-----------------------------------------------------------------------------\n");
return ;
}
//清空屏幕内容
void ClearWin(int clc_flag)
{
char c = 0;
printf("按任意键继续...");
c = getchar(); //接收回车键
if(0 == clc_flag) //判断是否需要重新接收一个回车键
c = getchar();
system("cls");
}
//是不是取消键
bool IsEsc(char key)
{
if(0x1b == key)
return true;
else
return false;
}
/*
名称:查询记录有关函数
说明:这里是有关查询类函数。
1.ShowOneRecord(int *clc_flag):这个函数的作用是查询一条学生的记录。需要由用户提供带查询记录的
学号。基本的过程时,当用户输入完待查询的记录后,程序会在变量stus.stu_num[]中查询学号是否存在,
若学号不存在,则提醒用户输入有误,并重新输入或退出本次操作。若学号存在,则读出文件中相应位置的
记录并显示出来。
2.ShowAllRecords():显示文件中所有的记录。按照顺序从文件开始读出所有的记录。
*/
#include<stdio.h>
#include "mian.h"
//查询一条学生记录
bool ShowOneRecord(int *clc_flag)
{
int stu_num = 0,index = -1;
int c = 0;
Student stu;
//输入学号
while(true)
{
printf("这位同学,请输入你要查询的学生的编号:");
scanf("%d",&stu_num);
if((index = SearStuPos(stu_num)) == -1)
{
printf("这位同学,不好意思,你查找的学生不存在!\n");
printf("请重新输入或者取消本次操作(按Esc键取消,其他键重新输入)...");
c = getchar(); //接受回车
c = getch(); //判断是否按下ESC键
if(IsEsc(c) == true)
{
printf("\n哎呀,取消本次操作了哦!\n");
*clc_flag = 1;
return false;
}
else
printf("\n");
}
else
break;
}
//获取学生学号在文件中的位置
//index = SearStuPos(stu_num);
//读取一条记录
if (File_ReadOneRecord(&stu,index) == false)
{
printf("读取一条学生记录失败!\n");
return false;
}
else
{
ShowBeginLine();
ShowStuInfo(&stu); //显示学生信息
ShowEndLine();
*clc_flag = 0;
return true;
}
}
//显示目前系统中的数量
void ShowCurStuNums()
{
int i = 0;
printf("目前系统中的学生数量为:%d.\n",stus.nums);
printf("他们的学号为:\n");
for( i = 1;i<=stus.nums;++i)
printf("%d: %d\n",i,stus.stu_nums[i-1]);
printf("\n");
}
//显示所有的学生信息
bool ShowAllRecords()
{
int i = 0;
Student temp_stus[stus.nums];
File_ReadAllRecords(&temp_stus); //从文件中读出所有的学生信息
ShowBeginLine();
//输出所有的学生信息
for(i = 0;i<stus.nums;++i)
{
ShowStuInfo(&temp_stus[i]);
printf("\n");
}
ShowEndLine();
return true;
}
mian.h
#ifndef MIAN_H_INCLUDED
#define MIAN_H_INCLUDED
#include <stdbool.h>
#include "stdlib.h"
#define MAX_STUDENT 1000 //最大的学生数量
//学生记录
typedef struct student
{
//char stu_num[20]; //学生编号
int stu_num; //学生编号
char name[20];
char sex;
int age;
int grade;
int _class;
}Student;
typedef struct students
{
int stu_nums[MAX_STUDENT]; //所有学生的编号
int nums; //学生数量
}Students;
extern Students stus;
//打印菜单
void PrintfMeau();
//用户选择
bool Choose(int *opt);
//显示学生信息
void ShowStuInfo(Student *stu);
//检查学号是否已经存在
bool StuNumIsExist(int stu_num);
//查找的学生的位置
int SearStuPos(int stu_num);
//添加一条学生记录
bool AddRecord();
//查询一条学生记录
bool ShowRecord();
//文件操作,添加一条学生记录
bool File_Add(Student *stu);
//修改学生信息
bool ModifyRecord();
//删除一个记录
bool DeleteOneRecord(int *clc_flag);
//文件初始化
bool File_Init();
//显示目前系统中的数量
void ShowCurStuNums();
//显示所有的学生信息
bool ShowAllRecords();
//退出时,将学生记录的数量写入student_num.dat文件
bool File_Exit();
//退出系统
bool Exit(int *flag);
//文件操作:删除所有记录
bool File_DeleteAllRecord();
//删除所有记录
bool DeleteAllRecord();
//清空屏幕内容
void ClearWin(int clc_flag);
//输出显示的结尾线
void ShowEndLine();
//输出显示的开头内容
void ShowBeginLine();
//是不是取消键
bool IsEsc(char key);
#endif // MIAN_H_INCLUDED
五、 谈谈数据的流向和存储。
在最后,我想谈谈在系统运行的整个过程中数据的流向和存储形式。
在我看来,整个系统运行的过程中,有四种数据流向和存储的方式。
1. 数据一直保存在外存文件中。这种方式中,数据记录文件是一直保持在外部文件中的。当执行某个功能模块的时候,比如说增加一条记录,为了确认增加记录的学号不会和文件中已经存在的记录学号相重复,必须要遍历整个文件。再比如说删除一条记录,也必须在文件中进行逐条记录的查询。这样才可以进行后序操作。因此,这种方式会有大量的I\O消耗。
2. 数据主体保存在外部文件中,数据一部分保存在内存中用作查询使用。这种方式一般把能够索引的部分(在数据库中好像叫做主键,本程序用的是学号)提取至内存中,在执行用户选择的功能的时候,有用到查询待选择记录在文件中的位置(或者判断用户选择的记录是否存在)的时候,直接在内存中的索引部分进行查询,然后根据位置在文件直接定位到相应的位置,进行读写操作。当然,如果是删除操作,还必须进行向前的覆盖操作,这会花费一定的I\O消耗。总的来说,如果针对查询占操作的主要部分,这种方式还是比较有效率的。本系统采用的就是这种方式。
3. 数据的主体部分在文件中,同时在内存中再存放一个数据的副本,即文件内容的全部拷贝。这种方式的话,所有的操作都可以在内存中进行操作,程序运行的速度可以大大的提高。但是,由于文件的内容要全部拷贝至内存,所以如果文件的内容较大,即要存储的数据非常大的时候,这会花费内存相当大的存储空间。而且初始化在拷贝内存文件的时候,会花费相当长的I\O时间消耗。所以这种方式一般用于小的文件,但是小的文件,查询内存带来的优势又不是太明显,故在我看来,这种方式在实际中用的较少了。
4. 数据的主体部分存储在文件中,采用内存和外部文件分离的方式。什么叫分离的方式呢?就是每次操作时只在内存中进行操作,和外部文件中没有关系(可以当作外部文件为空)。这样删除、查改、查询都只能在内存中进行。当进行存储的时候,在一次存储到外部文件中(在这个过程中要包括查询是否含有相同的主键),当然如果要查询文件中相应的记录也必须把文件中的内容提取至内存中。这种方式对于查询文件中的内容是,如果是大文件的话也会有大量的I\O消耗,其他方面因为是在先内存中处理,因此速度较快。总的来说,这种方式对于小文件、中等大小的文件的效率算是比较高的。
六、 谈谈注意事项:
1、 在使用本系统的时候,要保证系统运行目录下要么文件student.dat和student_num.dat文件爱你都存在,要么上述两个文件都不存在,否则可能会导致系统运行出错。
2、 其他还未检测到的错误。
具体的代码工程已经整理成压缩文件,有需要的可以前往下载,希望有所帮助。
http://download.csdn.net/download/plm199513100/10253301