兩周的時間完成了這個作業,學生成績管理系統。題目的要求是從文件中讀取班級成績原始記錄,文件內容包括ID、姓名、語文成績、數學成績、平均成績。要實現的功能有顯示學生成績信息,按照學生平均成績進行排序,錄入新的學生成績記錄,刪除學生成績記錄這4個主要的功能。
拿到這個題目之后,第一考慮就是用鏈表來做,鏈表的結點是一個結構體,把學生的個人信息和成績信息存放在這個結構體中。系統要求的功能也可以一一的轉化為對於鏈表的操作。
(1)顯示學生成績就可以轉化成循環鏈表,打印鏈表中每一個結點信息。
(2)按照學生平均成績進行排序可以轉化成,根據鏈表結點中平均成績的值對鏈表進行重新排序,排序可以用冒泡排序的方法。
(3)錄入新的學生成績記錄,可以轉化成插入結點操作。
(4)刪除學生成績記錄,可以轉化成刪除結點的操作。
1.系統界面和功能展示
根據題目的要求,設計了系統的主界面,
1、顯示學生成績信息
2、插入學生成績信息
3.刪除學生成績信息
4、按照平均成績排序
2、各功能代碼
(1)頭文件和結點結構體的定義
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <fcntl.h> #define MAX 10 typedef struct student * Student; static Student head; /*鏈表結點結構 *ID: 學生編號 *name: 學生姓名 *chgrade: 學生語文成績 *mathgrade:學生數學成績 *avegrade:學生平均成績 *next: 下一個結點指針 * */ struct student { Student next; int ID; char name[MAX]; float chgrade; float mathgrade; float avegrade; };
(2)主函數,主界面的顯示和各部分功能函數的調用
主函數主要就是界面的設計,主界面設計里一個主要的問題就是,一個功能實現之后,程序還是要運行,而不是退出程序,因此需要設計一個while循環,一直循環,只有用戶想要退出該界面的時候,選擇退出才退出。主函數中另外一個設計是判斷程序運行的時候是否選擇要打開的成績文件,如果沒有選擇要打開的成績文件,程序退出。該部分代碼如下:
int main(int argc, char *argv[ ]) { int number = 0; int count = 0; int ID; char name[MAX]; if(argc < 2) { printf("input wrong, please check\n"); exit(1); } read_file(argv[1]); /*循環顯示主選項單*/ while(number != 5) { printf("please select the option:\n"); printf("1.Display all student's info\n"); printf("2.Sort by average\n"); printf("3.Insert a new info\n"); printf("4.Delete a record\n"); printf("5.Quit\n"); printf("Please input your choices : \n"); scanf("%d", &number); if(number == 1) { print(); } if(number == 2) { ave(head); } if(number == 3) { insert(); } if(number == 4) { /*循環顯示刪除選項單*/ while(count != 3) { printf("Please choose the way\n"); printf("1.Delete by ID\n"); printf("2.Delete by name\n"); printf("3.exit\n"); printf("Your choice:"); scanf("%d", &count); if(count == 1) { printf("Pleas input the ID\n"); scanf("%d", &ID); del_ID(head,ID); } if(count == 2) { printf("Please input the name\n"); scanf("%s",name); printf("%s\n", name); del_name(head, name); } if(count < 0 || count >3) { printf("please input right number\n"); } } count = 0; } } return 0; }
(3)成績錄入,在鏈表中插入結點
插入結點函數先從外部輸入要插入結點的信息,然后進行插入鏈表的操作。本質上來說就是給結點賦值。先判斷頭結點是否為空,如果頭結點為空,直接將結點信息賦值給頭結點,頭結點指向空結點;如果頭結點不為空,找到鏈表中最后一個結點,將最后一個結點中的next指針指向要插入的結點,然后將要插入的信息賦值給新插入的結點,新插入的結點指向空,具體代碼如下所示。
int insert() { int ID; char name[MAX]; float chgrade; float mathgrade; float avegrade; int i; printf("Please input the new info (ID name chgrade mathgrade avegrade) : \n"); scanf("%d %s%f%f%f", &ID, &name, &chgrade, &mathgrade, &avegrade); printf("input %d %s %f %f %f successfully\n", ID, name ,chgrade, mathgrade, avegrade); printf("\n"); Student p, q; p = head; if(p != NULL) { while(p->next != NULL) { p = p->next; } } q = (Student)malloc(sizeof(struct student)); if(q == NULL) return -1; q->next = NULL; q->ID = ID; for(i = 0; i < MAX; i++) { q->name[i] = name[i]; } q->chgrade = chgrade; q->mathgrade = mathgrade; q->avegrade = avegrade; if(p == NULL) { head = q; return 1; } p->next = q; }
(4)排序,按照平均成績的高低排序
按照平均成績排序就將按照平均成績值的大小對鏈表進行冒泡排序,平均成績低的結點在前邊,平均成績高的結點在后邊。設置兩個結構體指針,一個被對比的,一個是對比的,對比的指針向后移,被對比的指針在對比的指針循環完一次之后向后挪一次。也就是兩個while循環,內循環循環對比的指針,是“冒泡”的過程,外循環循環被對比的指針。
int ave(struct student *head) { struct student *p1; struct student *p2; struct student *p3; int ID; float chgrade; float mathgrade; char name[MAX]; float avegrade; int count = 0; int i; if(head == NULL) { printf("no grades\n"); return head; } p1 = head; if(p1->next == NULL) { printf("success\n"); return head; } p2 = p1->next; while(p1->next != NULL) { while(p2 != NULL) { if(p1->avegrade < p2->avegrade) { p2 = p2->next; } else { ID = p2->ID; for(i = 0; i < MAX; i++) { name[i] = p2->name[i]; } mathgrade = p2->mathgrade; chgrade = p2->chgrade; avegrade = p2->avegrade; p2->ID = p1->ID; for(i = 0; i < MAX; i++) { p2->name[i] = p1->name[i]; } p2->chgrade = p1->chgrade; p2->mathgrade = p1->mathgrade; p2->avegrade = p1->avegrade; p1->ID = ID; //p1-> name = name; for(i = 0; i < MAX; i++) { p1->name[i] = name[i]; } p1->chgrade = chgrade; p1-> mathgrade = mathgrade; p1->avegrade = avegrade; p2 = p2->next; } } p1 = p1->next; p2 = p1->next; } print(); return head; }
(5)刪除結點,刪除學生信息,可以按照ID也可以按照姓名
按照學生ID刪除學生信息,遍歷鏈表查到鏈表中要刪除的結點,然后將該結點的前一個結點的next指針指向改結點的下一個結點,釋放該節點,代碼如下。
int del_ID(struct student *head, int ID) { struct student *p1; struct student *p2; if(head == NULL) { printf("no grades\n"); return head; } p1 = head; while(p1->ID != ID && p1->next != NULL) { p2 = p1; p1 = p1->next; } if(p1->ID == ID) { if(p1 == head) { head = p1->next; } else { p2->next = p1->next; } free(p1); p1 = NULL; printf("delete %d success\n", ID); } else { printf("%d not been found!\n"); } return head; } 按照學生姓名刪除學生信息跟按照ID刪除學生信息類似,區別在於遍歷鏈表的時候,一個是找ID相同的結點,一個是尋找姓名相同的結點,代碼如下。 int del_name(struct student *head, char name[MAX]) { struct student *p1; struct student *p2; if(head == NULL) { printf("no grades\n"); return head; } p1 = head; while(strcmp(p1->name,name) != 0 && p1->next != NULL) { p2 = p1; p1 = p1->next; } if(strcmp(p1->name,name) == 0) { if(p1 == head) { head = p1->next; } else { p2->next = p1->next; } free(p1); p1 = NULL; printf("delete %s successfully\n", name); } else { printf("%s not be found", name); } print(); return head; }
(6)打印鏈表,顯示學生成績
遍歷整個鏈表,分別按照一定的格式打印出鏈表中每個結點的信息,其中包括學生ID、學生姓名、學生數學成績、學生語文成績、學生平均成績,代碼如下。
int print(void) { int i = 1; Student p = head; printf("/***************************************************/\n"); printf("seq ID NAME CHGRADE MATHGRADE AVEGRADE\n"); while(p != NULL) { printf("%d: %d %s %f %f %f\n", i, p->ID, p->name, p->chgrade, p->mathgrade, p->avegrade); p = p->next; i++; } printf("/***************************************************/\n"); } (7)讀文件中的內容,插入鏈表 學生成績存在txt文件中,txt文件都是以字符串的形式存儲的,先將文件中的學生信息讀出來分別存在字符串中,然后將相應的字符串轉換成int型或者float型數字,然后將這些信息插入到鏈表中,具體代碼如下。 int read_file(char *s) { FILE *fp; int n,i,j,k; int m = -1; Student p, q; int ID; //學生ID char name[MAX]; //學生姓名 float chgrade; //學生語文成績 float mathgrade;//學生數學成績 float avegrade; //學生平均成績 char a[MAX]; //數組a,存儲從文件中讀出來的ID的字符串 char b[MAX]; //數組b,存儲從文件中讀出來的name的字符串 char c[MAX]; //數組c,存儲從文件中讀出來的chgrade的字符串 char d[MAX]; //數組d,存儲從文件中讀出來的mathgrade的字符串 char e[MAX]; //數組e,存儲從文件中讀出來的avegrade的字符串 fp = fopen(s, "r"); if(fp == NULL) { perror("fail to fopen"); exit(1); } while(j != -1) { j = fscanf(fp, " %[^ ] %[^ ] %[^ ] %[^ ] %[^ ]", a, b, c, d, e); m++; } fclose(fp); fp = fopen(s, "r"); if(fp == NULL) { perror("fail to fopen"); exit(1); } for(k = 0; k < m; k++) { n = fscanf(fp, " %[^ ] %[^ ] %[^ ] %[^ ] %[^ ]", a, b, c, d, e); ID = atoi(a); for(i = 0; i < MAX; i++) { name[i] = b[i]; } chgrade = atof(c); mathgrade = atof(d); avegrade = atof(e); p = head; if(p != NULL) { while(p->next != NULL) { p = p->next; } q = (Student)malloc(sizeof(struct student)); if(q == NULL) return -1; q->next = NULL; q->ID = ID; for(i = 0; i < MAX; i++) { q->name[i] = name[i]; } q->chgrade = chgrade; q->mathgrade = mathgrade; q->avegrade = avegrade; p->next = q; } else { q = (Student)malloc(sizeof(struct student)); if(q == NULL) return -1; q->next = NULL; q->ID = ID; for(i = 0; i < MAX; i++) { q->name[i] = name[i]; } q->chgrade = chgrade; q->mathgrade = mathgrade; q->avegrade = avegrade; head = q; } } }
兩周的時間完成了這個小的作業,感覺長進了很多,從以前的抄寫程序到自己寫程序的過程中也意識到考慮問題的不全面,但是也是這兩周一點一點的摸索找到了自己的解決問題的方法。編程在基礎東西掌握了之后,最后還是邏輯思維的區別。
最后,代碼綜合起來如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <fcntl.h> #define MAX 10 typedef struct student * Student; static Student head; /*鏈表結點結構 *ID: 學生編號 *name: 學生姓名 *chgrade: 學生語文成績 *mathgrade:學生數學成績 *avegrade:學生平均成績 *next: 下一個結點指針 * */ struct student { Student next; int ID; char name[MAX]; float chgrade; float mathgrade; float avegrade; }; /*成績錄入,在鏈表中插入結點*/ int insert() { int ID; char name[MAX]; float chgrade; float mathgrade; float avegrade; int i; printf("Please input the new info (ID name chgrade mathgrade avegrade) : \n"); scanf("%d %s%f%f%f", &ID, &name, &chgrade, &mathgrade, &avegrade); printf("input %d %s %f %f %f successfully\n", ID, name ,chgrade, mathgrade, avegrade); printf("\n"); Student p, q; p = head; if(p != NULL) { while(p->next != NULL) { p = p->next; } } q = (Student)malloc(sizeof(struct student)); if(q == NULL) return -1; q->next = NULL; q->ID = ID; for(i = 0; i < MAX; i++) { q->name[i] = name[i]; } q->chgrade = chgrade; q->mathgrade = mathgrade; q->avegrade = avegrade; if(p == NULL) { head = q; return 1; } p->next = q; } /*從文件中讀取數據,插入鏈表*/ int read_file(char *s) { FILE *fp; int n,i,j,k; int m = -1; Student p, q; int ID; //學生ID char name[MAX]; //學生姓名 float chgrade; //學生語文成績 float mathgrade;//學生數學成績 float avegrade; //學生平均成績 char a[MAX]; //數組a,存儲從文件中讀出來的ID的字符串 char b[MAX]; //數組b,存儲從文件中讀出來的name的字符串 char c[MAX]; //數組c,存儲從文件中讀出來的chgrade的字符串 char d[MAX]; //數組d,存儲從文件中讀出來的mathgrade的字符串 char e[MAX]; //數組e,存儲從文件中讀出來的avegrade的字符串 fp = fopen(s, "r"); if(fp == NULL) { perror("fail to fopen"); exit(1); } while(j != -1) { j = fscanf(fp, " %[^ ] %[^ ] %[^ ] %[^ ] %[^ ]", a, b, c, d, e); m++; } fclose(fp); fp = fopen(s, "r"); if(fp == NULL) { perror("fail to fopen"); exit(1); } for(k = 0; k < m; k++) { n = fscanf(fp, " %[^ ] %[^ ] %[^ ] %[^ ] %[^ ]", a, b, c, d, e); ID = atoi(a); for(i = 0; i < MAX; i++) { name[i] = b[i]; } chgrade = atof(c); mathgrade = atof(d); avegrade = atof(e); p = head; if(p != NULL) { while(p->next != NULL) { p = p->next; } q = (Student)malloc(sizeof(struct student)); if(q == NULL) return -1; q->next = NULL; q->ID = ID; for(i = 0; i < MAX; i++) { q->name[i] = name[i]; } q->chgrade = chgrade; q->mathgrade = mathgrade; q->avegrade = avegrade; p->next = q; } else { q = (Student)malloc(sizeof(struct student)); if(q == NULL) return -1; q->next = NULL; q->ID = ID; for(i = 0; i < MAX; i++) { q->name[i] = name[i]; } q->chgrade = chgrade; q->mathgrade = mathgrade; q->avegrade = avegrade; head = q; } } } /*按照平均成績排序 *使用的排序方法是冒泡排序 *兩個結構體指針,在冒泡過程中一個不動,一個向后挪與不動的指針進行比較 *外層while循環是向后移動冒泡的不動指針 *內層while循環是冒泡的過程 * */ int ave(struct student *head) { struct student *p1; struct student *p2; struct student *p3; int ID; float chgrade; float mathgrade; char name[MAX]; float avegrade; int count = 0; int i; if(head == NULL) { printf("no grades\n"); return head; } p1 = head; if(p1->next == NULL) { printf("success\n"); return head; } p2 = p1->next; /*while(p1 != NULL) { count++; p1 = p1->next; } printf("%d\n", count); p1 = head; p2 = p1->next;*/ while(p1->next != NULL) { while(p2 != NULL) { if(p1->avegrade < p2->avegrade) { p2 = p2->next; } else { ID = p2->ID; for(i = 0; i < MAX; i++) { name[i] = p2->name[i]; } mathgrade = p2->mathgrade; chgrade = p2->chgrade; avegrade = p2->avegrade; p2->ID = p1->ID; //p2->name = p1->name; for(i = 0; i < MAX; i++) { p2->name[i] = p1->name[i]; } p2->chgrade = p1->chgrade; p2->mathgrade = p1->mathgrade; p2->avegrade = p1->avegrade; p1->ID = ID; //p1-> name = name; for(i = 0; i < MAX; i++) { p1->name[i] = name[i]; } p1->chgrade = chgrade; p1-> mathgrade = mathgrade; p1->avegrade = avegrade; p2 = p2->next; } } p1 = p1->next; p2 = p1->next; } print(); return head; } /*按照學生ID刪除學生信息*/ int del_ID(struct student *head, int ID) { struct student *p1; struct student *p2; if(head == NULL) { printf("no grades\n"); return head; } p1 = head; while(p1->ID != ID && p1->next != NULL) { p2 = p1; p1 = p1->next; } if(p1->ID == ID) { if(p1 == head) { head = p1->next; } else { p2->next = p1->next; } free(p1); p1 = NULL; printf("delete %d success\n", ID); } else { printf("%d not been found!\n"); } return head; } /*按照學生姓名刪除學生信息*/ int del_name(struct student *head, char name[MAX]) { struct student *p1; struct student *p2; if(head == NULL) { printf("no grades\n"); return head; } p1 = head; while(strcmp(p1->name,name) != 0 && p1->next != NULL) { p2 = p1; p1 = p1->next; } if(strcmp(p1->name,name) == 0) { if(p1 == head) { head = p1->next; } else { p2->next = p1->next; } free(p1); p1 = NULL; printf("delete %s successfully\n", name); } else { printf("%s not be found", name); } print(); return head; } /*打印鏈表,顯示出學生成績*/ int print(void) { int i = 1; Student p = head; printf("/***************************************************/\n"); printf("seq ID NAME CHGRADE MATHGRADE AVEGRADE\n"); while(p != NULL) { printf("%d: %d %s %f %f %f\n", i, p->ID, p->name, p->chgrade, p->mathgrade, p->avegrade); p = p->next; i++; } printf("/***************************************************/\n"); } /*主函數*/ int main(int argc, char *argv[ ]) { int number = 0; int count = 0; int ID; char name[MAX]; if(argc < 2) { printf("input wrong, please check\n"); exit(1); } read_file(argv[1]); /*循環顯示主選項單*/ while(number != 5) { printf("please select the option:\n"); printf("1.Display all student's info\n"); printf("2.Sort by average\n"); printf("3.Insert a new info\n"); printf("4.Delete a record\n"); printf("5.Quit\n"); printf("Please input your choices : \n"); scanf("%d", &number); if(number == 1) { print(); } if(number == 2) { ave(head); } if(number == 3) { insert(); } if(number == 4) { /*循環顯示刪除選項單*/ while(count != 3) { printf("Please choose the way\n"); printf("1.Delete by ID\n"); printf("2.Delete by name\n"); printf("3.exit\n"); printf("Your choice:"); scanf("%d", &count); if(count == 1) { printf("Pleas input the ID\n"); scanf("%d", &ID); del_ID(head,ID); } if(count == 2) { printf("Please input the name\n"); scanf("%s",name); printf("%s\n", name); del_name(head, name); } if(count < 0 || count >3) { printf("please input right number\n"); } } count = 0; } } return 0; }