版权声明:本文为Cantjie原创文章,转载需获得博主许可 https://blog.csdn.net/cantjie/article/details/71244651
最后,将写完的代码贴上来
一点说明:
1、目前还有一些bug,在ID被判重复后输入0,有时会使得程序崩溃,时间原因尚未解决
2、中英文的注释混杂,并不是一个好习惯。写的过程中试图让自己以全英文注释,并严格按格式注释。但由于时间关系或英文水平的限制,既没有按照格式来注释,也没有做到全英文。
3、链表的建立、validate并不是很让我满意,感觉还有可以优化的地方。
4、sort用了选择排序,find只是逐个比较来查找,并没有学到什么新的算法,也没有搜一搜适合链表的查找算法。这里或许可以做很多优化。
5、文件的读写过于频繁。还不会随机读写,或许这里也可以进一步优化。
6、之所以主界面里面没有单独的排序功能,是因为添加、更新数据后都会重新排序一次并保存在文件中。
7、交互界面并不令我满意。很简陋。
8、在前期没有把函数的实现方法想明白,导致main.c中controller函数很复杂。或许可以改进一下delete、update等函数,使得代码更简洁,重复代码更少。并且使得variance、delete、update等函数用法更加一致。
9、程序写完之后才意识到没有输出均值,于是临时添加全局变量m_ave。
10、在学生姓名发生重复时,无法同时查找到这些学生。
——————————————
20170508修复了update时无法更新的问题。
——————————————
20170520结构体可以用=直接赋值,可以将swap函数省略了但还没有改
——————————————
app.c
#include "final.h"
/**
*selectsort by cls & sub-by name
*by cantjie
*/
stu *sort(stu *head)
{
//遍历链表,每次找出一个最小的节点,将其值与未排序节点的首个节点交换,这里需要一个指针标记值最小的节点。
stu *p1, *p2, *min;
for (p1 = head->next; p1->next != NULL; p1 = p1->next)
{
min = p1;
for (p2 = p1->next; p2; p2 = p2->next)//由前向后遍历,找出最小的节点
{
if (p2->cls<min->cls)
min = p2;
else
{
if (p2->cls == min->cls)
{
if (strcmp(p2->name, p1->name) < 0)
{
swapStu(p1, p2);
}
}
}
}
if (min != p1)
swapStu(p1, min);
}
saveStu(head->next, 0);
m_head = head;
return head;
}
/**
*检查id 是否重复,如果id重复,返回1。不重复返回0
*/
int validate(stu *head, int id)
{
stu *p = head;
int flag = 0;//因为会遇到自己,设置一个flag,当flag为2时表示有重复;
while (p->next)
{
if (p->next->id == id)
{
if (flag)
return 1;
flag++;
}
p = p->next;
}
if (m_head != head)
{
p = m_head;
if (p == NULL)
return 0;
while (p->next)
{
if (p->next->id == id)
return 1;
else
p = p->next;
}
return 0;
}
}
/**
*计算方差,包含三种格式:
*1、输入0,返回所有学生方差;
*2、输入1,输入cls,返回该班学生的方差;
*3、输入2,然后输入id范围,返回方差;
*by cantjie
*/
float varianceStu(stu *head)
{
int flag = -1;
int n = 0;
int idmax=0, idmin=0;//for case 2
int id = 0;//for case 3
float var=0, sum=0, ave=0;
int i = 0;
stu *p = head;
printf("\ninput 0 to get all students' variance");
printf("\ninput 1 to get variance of students of a class");
printf("\ninput 2 to get varicance of students of a range of ids\n");
scanf("%d", &flag);
switch (flag)
{
case 0://全体方差
for (i = 0; i < m_n; i++)
{
sum += p->next->sum;
p = p->next;
}
ave = sum / m_n;
p = head;
for (i = 0; i < m_n; i++)
{
var += (p->next->sum - ave)*(p->next->sum - ave);
p = p->next;
}
var /= m_n;
break;
case 1://返回班级方差
printf("\nPlease input class:");
int cls = 0;
scanf("%d", &cls);
for (i = 0; i < m_n; i++)
{
if (p->next->cls == cls)
{
sum += p->next->sum;
n++;
}
p = p->next;
}
ave = sum / n;
p = head;
for (i = 0; i < m_n; i++)
{
if (p->next->cls == cls)
{
var += (p->next->sum - ave)*(p->next->sum - ave);
}
p = p->next;
}
var /= n;
break;
case 2://id范围
printf("\nPlease input the range of ID:(from...to...)");
scanf("%d%d", &idmin, &idmax);
if (idmin > idmax)
return 0;
p = head; n = 0;
for (i = 0; i < m_n; i++)
{
if (p->next->id >= idmin && p->next->id <= idmax)
{
sum += p->next->sum;
n++;
}
p = p->next;
}
ave = sum / n;
p = head;
for (i = 0; i < m_n; i++)
{
if (p->next->id >= idmin && p->next->id <= idmax)
{
var += (p->next->sum - ave)*(p->next->sum - ave);
}
p = p->next;
}
var /= n;
break;
//这个功能有问题。时间不够,放弃了
/* case 3://连续输入ID
printf("\nPlease input student id:(input 0 to stop input)");
while (scanf("%d", &id))
{
if (id == 0) break;
for (i = 0; i < m_n; i++)
{
if (p->next->id == id)
{
n++;
sum += p->next->sum;
}
p = p->next;
}
ave = sum / n;
p = head;
for (i = 0; i < m_n; i++)
{
if (p->next->id == id)
{
var += (p->next->sum - ave)*(p->next->sum - ave);
}
}
var /= n;
}
break;*/
default:
break;
}
m_ave=ave;
return var;
}
/**
*by cantjie
*/
stu *create()//创建或写入新的学生数据
{
int i = 0;
stu *head = NULL;//指向链表头,链表头一般不存信息。
stu *p1 = NULL, *p2 = NULL;//p1指向新创建的链表,p2是原链表的尾部
p1 = (stu *)malloc(LEN);
if (p1 == NULL)
{
printf("\nFail to create it,please try again later.\n");
return NULL;
}
head = p1;
head->id = -1;
p2 = p1 = (stu *)malloc(LEN);
if (p1 == NULL)
{
printf("\nFail to create it,please try again later.\n");
return NULL;
}
else
{
head->next = p1;
p1->next = NULL;
printf("\nPlease input student ID(0 to stop):");
scanf("%d", &(p1->id));
}
if (p1->id == 0)
{
return 0;
}
while (p1->id != 0)
{
p2->next = p1;
p1->next = NULL;//通过调试可以发现如果不加这两句命令,连着输入两次相同ID,validate无法检测出来
if (validate(head, p1->id))
{
printf("\nThis id has already existed,please try again.");
printf("\nPlease input student ID(0 to stop):");
scanf("%d", &(p1->id));
if (p1->id == 0) p2->next = NULL;//与上面那句命令对应
continue;
}
else
{
p2->next = p1;
p2 = p1;
printf("\nPlease input class:");
scanf("%d", &(p1->cls));
printf("\nPlease input name(no more than 20 characters):");
//scanf("%s", p1->name); 如果用scanf的话,无法保存空格
fflush(stdin);//当gets前面有scanf时,scanf余留下来的换行符会被gets读取,因此用fflush(stdin)刷新缓存区。
gets(p1->name);
printf("\nPlease input scores of 3 subjects:");
p1->sum = 0;
for (i = 0; i < 3; i++)
{
scanf("%f", &(p1->score[i]));
p1->sum += p1->score[i];
}
m_n++;
p2->next = NULL;
p1 = (stu *)malloc(LEN);//下面free的是这个p1
if (p1 == NULL)
{
printf("\nFail to create it,please try again later.\n");
return NULL;
}
else
{
printf("\nPlease input student ID(0 to stop):");
scanf("%d", &(p1->id));
}
}
}
free(p1);
saveStu(head->next, 1);
return sort(readStu());
}
/**
*显示学生信息
*/
void showStu(stu *p)
{
if (p == NULL)
{
printf("No such a student");
return;
}
printf("\n%-20s|%5d|%5d|%6.1f|%6.1f|%6.1f|%5.1f", p->name, p->id, p->cls, p->score[0], p->score[1], p->score[2], p->sum);
}
/**
*删除学生信息
*传入的参数为要删除的学生的前一个学生的指针
*by cantjie
*/
void deleteStu(stu *p)
{
if (p == NULL)
{
printf("\nNo such a student.");
return;
}
stu *temp = p->next;
p->next = p->next->next;
free(temp);
saveStu(m_head->next, 0);
m_n--;
}
/**
*更新、修改学生信息
*传入的参数为要更改的学生的指针
*by cantjie
*/
void updateStu(stu *p)
{
if (p == NULL)
{
printf("\nNo such a student");
return;
}
showTitle();
showStu(p);
int i, temp, temp2;
printf("\nPlease input student ID:");
scanf("%d", &temp);
if (temp == 0)
return;
if (temp != 0 && temp != p->id)
{
while (validate(m_head, temp))
{
printf("\nThis id has already existed,please try again.");
printf("\nPlease input student ID(0 to stop):");
scanf("%d", &temp);
if (temp == 0)
return;
if (temp == p->id)
break;
}
}
p->id = temp;
printf("\nPlease input class:");
scanf("%d", &(p->cls));
printf("\nPlease input name(no more than 20 characters):");
fflush(stdin);//当gets前面有scanf时,scanf余留下来的换行符会被gets读取,因此用fflush(stdin)刷新缓存区。
gets(p->name);
printf("\nPlease input scores of 3 subjects:");
p->sum = 0;
for (i = 0; i < 3; i++)
{
scanf("%f", &(p->score[i]));
p->sum += p->score[i];
}
saveStu(m_head->next, 0);
sort(m_head);
}
——————————————
common.c
#include "final.h"
/**
*通过学号或者姓名查找学生,用在其他函数中
*返回该学生前一个学生的指针
*重名问题暂时还没有解决,只能返回一个学生
*by cantjie
*/
stu *findStu(stu *head, int id, char *name)
{
if (id == 0)
{
while (head->next)
{
if (strcmp(head->next->name, name) == 0)
return head;
else
head = head->next;
}
//printf("Cannot find a student named %s", name);
return NULL;
}
else
{
while (head ->next)
{
if (head->next->id == id)
return head;
else
head = head->next;
}
//printf("Cannot find a student whose id is %d", id);
return NULL;
}
return 0;
}
/**
*swap the two Stu structure
*by cantjie
*/
void swapStu(stu *p1, stu *p2)
{
int tempi;
float tempf;
char tempc[20];
int i = 0;
tempi = p1->id;
p1->id = p2->id;
p2->id = tempi;
tempi = p1->cls;
p1->cls = p2->cls;
p2->cls = tempi;
strcpy(tempc, p1->name);
strcpy(p1->name, p2->name);
strcpy(p2->name, tempc);
for (i = 0; i < 3; i++)
{
tempf = p1->score[i];
p1->score[i] = p2->score[i];
p2->score[i] = tempf;
}
tempf = p1->sum;
p1->sum = p2->sum;
p2->sum = tempf;
}
——————————————
file.c
#include "final.h"
/**
*每次删除或更新等操作后,要把内存里的数据写入文件,flag表示模式,为1表示追加,为0表示重写
*put data in memory into _FILENAME_ in harddisk,used every time update or delete the data
*by cantjie
*/
void saveStu(stu *head, int flag)
{
if (!head)
{
return;
}
FILE *fp = NULL;
if (flag == 1)
fp = fopen(_FILENAME_, "ab");
else
{
if (flag == 0)
fp = fopen(_FILENAME_, "wb");
}
if (fp == NULL)
{
fp = fopen(_FILENAME_, "wb");
if (fp == NULL)
{
printf("\nCannot open or create the file");
return;
}
}
do
{
fwrite(head, sizeof(stu), 1, fp);
head = head->next;
} while (head);
fclose(fp);
}
/**
*把文件里的数据读取到内存中
*put data in harddisk into memory
*by cantjie
*/
stu *readStu()
{
stu *head = NULL, *tail = NULL, *p1 = NULL;//tail是原链表的尾,p1是新开辟的空间
FILE *fp;
fp = fopen(_FILENAME_, "rb");
if (fp == NULL)
{
fp = fopen(_FILENAME_, "wb");
if (fp == NULL)
printf("Fail to open or create %s in function readStu", _FILENAME_);
return NULL;
}
m_n = 0;
p1 = (stu *)malloc(LEN);
if (p1 == NULL)
{
printf("Fail to allocate memory for a new stu structure in function readStu.");
fclose(fp);//如果没有成功开辟内存,还要把文件关闭
return 0;
}
head = tail = p1;
tail->next = NULL;
while (!feof(fp))
{
p1 = (stu *)malloc(LEN);
if (p1 == NULL)
{
printf("Fail to allocate memory for a new stu structure in function readStu.");
fclose(fp);//如果没有成功开辟内存,还要把文件关闭
return 0;
}
if (!fread(p1, sizeof(stu), 1, fp))
{
free(p1);
return head;
}
tail->next = p1;
tail = p1;
tail->next = NULL;//保证尾指向空
m_n++;
}
fclose(fp);
m_head = head;
return head;
}
——————————————
main.c
#include "final.h"
#include<math.h>
/**
*显示学生信息前显示表头
*/
void showTitle()
{
printf("\n%-20s|%5s|%5s|score1|score2|score3|sum", "name", "ID", "class", "score1", "score2", "score3", "sum");
}
/**
*用在main函数,输入功能后进入此函数
*/
void controller(int flag)
{ //通过这里面的代码,或许可以考虑重新写一下findStu或重写deleteStu,updateStu
int i = 0;
int a = 0;
stu *temp;
float var;
char name[20];
switch (flag)
{
case 1:
if (m_head == NULL || m_head->next == NULL)
{
printf("\nNo student data.");
break;
}
temp = m_head;
printf("\ninput 0 to show all students' information");
printf("\nor input 1 and then an id or 2 and then a name to show one:");
scanf("%d", &a);
if (a == 0)
{
showTitle();
while (temp->next)
{
showStu(temp->next);
temp = temp->next;
}
}
else
{
if (a == 1)
{
printf("id:");
scanf("%d", &a);
if (a == 0)
{
printf("\n0 is not an legal ID.");
break;
}
temp = findStu(m_head, a,NULL );
if (temp == NULL)
{
printf("\nNo such a student");
break;
}
showTitle();
showStu(temp->next);
}
else
{
if (a == 2)
{
printf("name:");
fflush(stdin);
gets(name);
temp = findStu(m_head, 0, name);
if (temp == NULL)
{
printf("\nNo such a student");
break;
}
showTitle();
showStu(temp->next);
}
}
}
break;
case 2:
create();
break;
case 3:
if (m_head == NULL || m_head->next == NULL)
{
printf("\nNo student data.");
break;
}
temp = m_head;
printf("\ninput 1 and then an id or 2 and then a name to delete:");
scanf("%d", &a);
if (a == 1)
{
printf("id:");
scanf("%d", &a);
if (a == 0)
{
printf("\n0 is not an legal ID.");
break;
}
deleteStu(findStu(m_head, a, NULL));
}
else
{
if (a == 2)
{
printf("name:");
fflush(stdin);
gets(name);
deleteStu(findStu(m_head, 0, name));
}
}
break;
case 4:
if (m_head == NULL || m_head->next == NULL)
{
printf("\nNo student data.");
break;
}
temp = m_head;
printf("\ninput 1 and then an id or 2 and then a name to update:");
scanf("%d", &a);
if (a == 1)
{
printf("id:");
scanf("%d", &a);
updateStu(findStu(m_head, a, NULL)->next);
}
else
{
if (a == 2)
{
printf("name:");
fflush(stdin);
gets(name);
updateStu(findStu(m_head, 0, name)->next);
}
}
break;
case 5:
if (m_head == NULL || m_head->next == NULL)
{
printf("\nNo student data.");
break;
}
var=varianceStu(m_head);
printf("average:%f,variance:%f,Standard Deviation:%f", m_ave,var, sqrt(var));
break;
case 6:
exit(0);
}
}
/**
*print function list
*by cantjie
*/
void printFunctionList()
{
printf("\n_____________________________________");
printf("\n1.print students' information");
printf("\n2.add a new student");
printf("\n3.delete a student");
printf("\n4.update a student");
printf("\n5.show the analysis of students' grade");
printf("\n6.exit");
printf("\nInput a number to choose function:");
}
/**
*let user to choose function,after printing function list
*by cantjie
*/
void chooseFunction()
{
int flag = 0;
printFunctionList();
while (scanf("%d", &flag))
{
if (flag < 1 && flag>6)
{
printf("\nError!");
printFunctionList();
continue;
}
else
{
controller(flag);
printFunctionList();
}
}
}
int main()
{
m_head=readStu();
chooseFunction();
return 0;
}