#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    int rank;       // 排名序号
    char num[20];     // 学号
    char name[10];    // 姓名
    int math_score;   // 高数成绩
    int datastruct_score; // 数据结构成绩
    int English_score;    // 英语成绩
    int physics_score;    // 物理成绩
    float average;        // 平均分
} Student;

typedef struct {
    Student *data;
    int length;
} funlist;

// 初始化线性表
void initlist(funlist *l) {
    l->data = (Student *)malloc(100 * sizeof(Student));
    l->length = 0;
}

// 判断是否为空表
int isempty(funlist *l) {
    return l->length == 0;
}

//输出一定数量的空格 
void printspace(){
	int i;
	for(i=0;i<60;i++){
		printf(" ");
	}
} 

// 计算平均分
float calculate_average(Student *s) {
    return (s->math_score + s->datastruct_score + s->English_score + s->physics_score) / 4.0f;
}

// 查找操作（按学号查找）二进制文件 
int find_by_num(char *num) {
    struct student_type{
    	int rank;       // 排名序号
    	char num[20];     // 学号
    	char name[10];    // 姓名
    	int math_score;   // 高数成绩
    	int datastruct_score; // 数据结构成绩
    	int English_score;    // 英语成绩
    	int physics_score;    // 物理成绩
    	float average;        // 平均分
	} Student;

	FILE *fp;
	if((fp=fopen("studinfo.dat","rb+"))==NULL){	 
		printf("cannot open file\n");
		return 0;
	 } 
	while(!feof(fp))
   {
    if(fread(&Student,sizeof(struct student_type),1,fp))
	 {
        if(strcmp(num,Student.num)==0)
        {
		    printf("这个学生的信息为：\n");
		  	printf("学号:%-20s 姓名:%-10s 高数:%-3d 数据结构:%-3d 英语:%-3d 物理:%-3d 平均分:%-4.2f\n",Student.num,Student.name,Student.math_score,Student.datastruct_score,Student.English_score,Student.physics_score,Student.average); 
            system("pause");
     		fclose(fp);	  
     		
			return 0;
	    }	
   	 }

   }
   	printf("该学生信息不存在!\n");	
	fclose(fp);	 
	return 0; 
}

// 查找操作（按姓名查找）二进制文件 
int find_by_name(char *name) {
    struct student_type{
    	int rank;       // 排名序号
    	char num[20];     // 学号
    	char name[10];    // 姓名
    	int math_score;   // 高数成绩
    	int datastruct_score; // 数据结构成绩
    	int English_score;    // 英语成绩
    	int physics_score;    // 物理成绩
    	float average;        // 平均分
	} Student;

	FILE *fp;
	if((fp=fopen("studinfo.dat","rb+"))==NULL){
		printf("cannot open file\n");
		return 0;
	 } 
	while(!feof(fp))
   {
    if(fread(&Student,sizeof(struct student_type),1,fp))
	 {
        if(strcmp(name,Student.name)==0)
        {
		    printf("这个学生的信息为：\n");
		  	printf("学号:%-20s 姓名:%-10s 高数:%-3d 数据结构:%-3d 英语:%-3d 物理:%-3d 平均分:%-4.2f\n",Student.num,Student.name,Student.math_score,Student.datastruct_score,Student.English_score,Student.physics_score,Student.average); 
            system("pause");
     		fclose(fp);	  
     		
			return 0;
	    }	
   	 }

   }
   	printf("该学生信息不存在!\n");	
	fclose(fp);	 
	return 0; 
}

//修改操作(按学号修改) (studinfo.dat二进制文件内容） 
int revisestudinfo_by_num(char *num){
	   struct student_type{
    	int rank;       // 排名序号
    	char num[20];     // 学号
    	char name[10];    // 姓名
    	int math_score;   // 高数成绩
    	int datastruct_score; // 数据结构成绩
    	int English_score;    // 英语成绩
    	int physics_score;    // 物理成绩
    	float average;        // 平均分
	} Student;
	FILE *fp;
	long offset=0;   //定义一个变量offset,用于存放文件位置标记的偏移量 
	if((fp=fopen("studinfo.dat","rb+"))==NULL){	//对二进制文件进行读写操作 
		printf("cannot open file\n");
		return 0;
	 } 
   while(!feof(fp))
   {
    if(fread(&Student,sizeof(struct student_type),1,fp))
	  {
        if(strcmp(num,Student.num)==0)
          {
		    printf("原来这个学生的信息为：\n");
		  	printf("学号:%-20s 姓名:%-10s 高数:%-3d 数据结构:%-3d 英语:%-3d 物理:%-3d 平均分:%-4.2f\n",Student.num,Student.name,Student.math_score,Student.datastruct_score,Student.English_score,Student.physics_score,Student.average); 
		    offset=ftell(fp);     //获取文件位置标记的当前位置，这个位置的值是一个字节数（从文件开头到当前位置的位移量大小。） 
	        //fseek(fp,-sizeof(struct student_type),SEEK_CUR);   //把文件指针定位到这行信息的开头，SEEK_CUR常量表示文件末尾位置，也可用2表示 
		   	fseek(fp,offset-sizeof(struct student_type),SEEK_SET);   //把文件指针定位到这行信息的开头，SEEK_SET常量表示文件开始位置，也可用0表示
		   	printf("请输入修改的信息:\n");
			printf("请输入姓名:");
            scanf("%s",Student.name);
            printf("请输入学号:");
            scanf("%s",Student.num);
       		printf("请输入高数成绩:");
            scanf("%d",&Student.math_score);
            printf("请输入数据结构成绩:");
            scanf("%d", &Student.datastruct_score);
            printf("请输入英语成绩:");
            scanf("%d", &Student.English_score);
            printf("请输入物理成绩:");
            scanf("%d", &Student.physics_score);
            Student.average=(Student.math_score+Student.datastruct_score+Student.English_score+Student.physics_score)/4.0;
			//Student.average=calculate_average(st);      //调用calculate_average函数计算平均分 
			 
		    fwrite(&Student,sizeof(struct student_type),1,fp);  //重新修改这个学生信息并存入原来位置 
		    printf("修改成功！"); 
           
            system("pause");
     		fclose(fp);	  
     		
			return 0;
		}	
   	 }

   }
   	printf("该学生信息不存在!\n");	
	fclose(fp);	 
	return 0; 
}

//修改操作(按姓名修改) (studinfo.dat二进制文件内容） 
int revisestudinfo_by_name(char *name){
	   struct student_type{
    	int rank;       // 排名序号
    	char num[20];     // 学号
    	char name[10];    // 姓名
    	int math_score;   // 高数成绩
    	int datastruct_score; // 数据结构成绩
    	int English_score;    // 英语成绩
    	int physics_score;    // 物理成绩
    	float average;        // 平均分
	} Student;
	FILE *fp;
	long offset=0;   //定义一个变量offset,用于存放文件位置标记的偏移量 
	if((fp=fopen("studinfo.dat","rb+"))==NULL){	//对二进制文件进行读写操作 
		printf("cannot open file\n");
		return 0;
	 } 
   while(!feof(fp))
   {
    if(fread(&Student,sizeof(struct student_type),1,fp))
	  {
        if(strcmp(name,Student.name)==0)
          {
		    printf("原来这个学生的信息为：\n");
		  	printf("学号:%-20s 姓名:%-10s 高数:%-3d 数据结构:%-3d 英语:%-3d 物理:%-3d 平均分:%-4.2f\n",Student.num,Student.name,Student.math_score,Student.datastruct_score,Student.English_score,Student.physics_score,Student.average); 
		    offset=ftell(fp);     //获取文件位置标记的当前位置，这个位置的值是一个字节数（从文件开头到当前位置的位移量大小。） 
	        //fseek(fp,-sizeof(struct student_type),SEEK_CUR);   //把文件指针定位到这行信息的开头，SEEK_CUR常量表示文件末尾位置，也可用2表示 
		   	fseek(fp,offset-sizeof(struct student_type),SEEK_SET);   //把文件指针定位到这行信息的开头，SEEK_SET常量表示文件开始位置，也可用0表示
		   	printf("请输入修改的信息:\n");
			printf("请输入姓名:");
            scanf("%s",Student.name);
            printf("请输入学号:");
            scanf("%s",Student.num);
       		printf("请输入高数成绩:");
            scanf("%d",&Student.math_score);
            printf("请输入数据结构成绩:");
            scanf("%d", &Student.datastruct_score);
            printf("请输入英语成绩:");
            scanf("%d", &Student.English_score);
            printf("请输入物理成绩:");
            scanf("%d", &Student.physics_score);
            Student.average=(Student.math_score+Student.datastruct_score+Student.English_score+Student.physics_score)/4.0;
			//Student.average=calculate_average(st);      //调用calculate_average函数计算平均分 
			 
		    fwrite(&Student,sizeof(struct student_type),1,fp);  //重新修改这个学生信息并存入原来位置 
		    printf("修改成功！"); 
           
            system("pause");
     		fclose(fp);	  
     		
			return 0;
		}	
   	 }

   }
   	printf("该学生信息不存在!\n");	
	fclose(fp);	 
	return 0; 
}

// 删除操作(按学号删除) 
int deleteelem(char *num) {
    char oldname[]="studinfo_temp.dat";       //定义一个临时文件 
	char newname[]="studinfo.dat";
	struct student_type{
    int rank;       // 排名序号
    char num[20];     // 学号
    char name[10];    // 姓名
    int math_score;   // 高数成绩
    int datastruct_score; // 数据结构成绩
    int English_score;    // 英语成绩
    int physics_score;    // 物理成绩
    float average;        // 平均分
} Student;
	FILE *tfp;
	FILE *fp;
	int i;
	if((fp=fopen("studinfo.dat","rb"))==NULL){      //打开studinfo.dat文件 ，以只读方式打开，b参数代表二进制文件 
		printf("cannot open file\n");
		return 0;
	} 
	tfp=fopen("studinfo_temp.dat","ab");    //打开临时文件studinfo_temp ，以追加方式打开，b参数代表二进制文件 
	while(!feof(fp))
	{
		if(fread(&Student,sizeof(struct student_type),1,fp)){
			if(strcmp(num,Student.num)!=0){
				if(fwrite(&Student,sizeof(struct student_type),1,tfp)!=1)
		  			printf("file write error\n");
			}
		}
    }  
	fclose(fp);
	fclose(tfp); 
	remove(newname);
	if(rename(oldname,newname)==0){
		printf("删除成功!\n");
	}	 
	else
		printf("删除失败!\n");
   return 1;				
}

// 插入操作
int listinsert(funlist *l, int i, Student e) {
    if (i < 1 || i > l->length + 1) {
        printf("位置不合法\n");
        return 0;
    }
    if (l->length >= 100) {
        printf("线性表已满\n");
        return 0;
    }
    int j;
    for (j = l->length - 1; j >= i - 1; j--) {
        l->data[j + 1] = l->data[j];
        // 向后移动排名
        l->data[j + 1].rank = l->data[j].rank + 1;
    }
    // 计算并保存平均分
    e.average = calculate_average(&e);
    e.rank = i;
    l->data[i - 1] = e;
    l->length++;
    return 1; // 返回1表示成功
}

// 按平均分从小到大排序（冒泡法） 
void sort_by_average(){
      struct student_type{
    	int rank;       // 排名序号
    	char num[20];     // 学号
    	char name[10];    // 姓名
    	int math_score;   // 高数成绩
    	int datastruct_score; // 数据结构成绩
    	int English_score;    // 英语成绩
    	int physics_score;    // 物理成绩
    	float average;        // 平均分
	}Stud[100];
	FILE *fp;
	int record=0;
	int i,j;
	struct student_type temp;
	if((fp=fopen("studinfo.dat","rb"))==NULL)      //打开studinfo.dat文件 ，以只读方式打开，b参数代表二进制文件 
	{
		printf("cannot open file\n");
		return;
	 } 
	while(!feof(fp))
	{   
		if(fread(&Stud[record],sizeof(struct student_type),1,fp)){
			record++;
		}
	}
	for(i=0;i<record-1;i++){	//使用冒泡排序进行降序排序 
		for(j=0;j<record-1-i;j++){
			if(Stud[j].average>Stud[j+1].average){
				temp=Stud[j];
				Stud[j]=Stud[j+1];
				Stud[j+1]=temp;
			}
		}
	}
	printf("按平均分从小到大排名如下:\n");
	for(i=0;i<record;i++){
		Stud[i].rank=i+1;
	}
	for(i=0;i<record;i++)
	printf("排名:%-4d 学号:%-20s 姓名:%-10s 高数:%-3d 数据结构:%-3d 英语:%-3d 物理:%-3d 平均分:%-4.2f\n",Stud[i].rank,Stud[i].num,Stud[i].name,Stud[i].math_score,Stud[i].datastruct_score,Stud[i].English_score,Stud[i].physics_score,Stud[i].average); 	  
    fclose(fp);
}
			
// 将数据添加到二进制文件，文件名为studinfo.dat 
void save_binfile(funlist l, const char *filename) {
    FILE *file = fopen(filename, "ab");     //以追加写模式打开二进制文件 
    if (file == NULL) {
        printf("无法打开文件\n");
        return;
    }

    for (int i = 0; i < l.length; i++) {
         if(fwrite(&l.data[i],sizeof(Student),1,file)!=1)
		  printf("file write error\n");
	}

    fclose(file);
    printf("数据已成功写入文件 %s\n", filename);
}

//显示所有信息(studinfo.dat的内容） 
void liststudinfo()
{
	struct student_type{
    int rank;       // 排名序号
    char num[20];     // 学号
    char name[10];    // 姓名
    int math_score;   // 高数成绩
    int datastruct_score; // 数据结构成绩
    int English_score;    // 英语成绩
    int physics_score;    // 物理成绩
    float average;        // 平均分
} Student;

	FILE *fp;
	int i;
	if((fp=fopen("studinfo.dat","rb"))==NULL)      //打开studinfo.dat文件 ，以只读方式打开，b参数代表二进制文件 
	{
		printf("cannot open file\n");
		return;
	 } 
	 
	while(!feof(fp))
	{
		if(fread(&Student,sizeof(struct student_type),1,fp))
		  printf("学号:%-20s 姓名:%-10s 高数:%-3d 数据结构:%-3d 英语:%-3d 物理:%-3d 平均分:%-4.2f\n",Student.num,Student.name,Student.math_score,Student.datastruct_score,Student.English_score,Student.physics_score,Student.average); 	  
	}	
	fclose(fp);	  	
}



int main() {
	funlist L;
    initlist(&L); 

    int choice;
    char num[20];
    char name[10];
    int math_score;
    int datastruct_score;
    int English_score;
    int physics_score;
    int i;
    printf("\n");
    printspace();printf("********学生信息管理系统********\n"); 
    printspace();printf("        程序制作：吴子赫\n"); 
        printf("\n\n");
 	printspace();printf("1.显示全部学生信息\n"); 
	printspace();printf("2.添加学生信息\n");
	printspace();printf("3.保存添加数据\n");
    printspace();printf("4.删除学生信息\n");
    printspace();printf("5.通过学号查询学生信息\n"); 
    printspace();printf("6.通过姓名查询学生信息\n"); 
    printspace();printf("7.通过学号修改学生信息\n");
    printspace();printf("8.通过姓名修改学生信息\n");
	printspace();printf("9.按平均分升序排列\n");
    printspace();printf("10.退出系统\n");
    printspace();printf("================================");
    printf("\n\n");
    
    while (1) { 
        printf("请输入操作选项:");
        scanf("%d", &choice);
        switch (choice) {
           	case 1:{
				liststudinfo();  //显示全部学生信息 studinfo.dat的内容 
				printf("\n");
				break;
			} 
			
		    case 2: {
                Student e;
                printf("请输入姓名:");
                scanf("%s", name);
                strcpy(e.name, name);
                
                printf("请输入学号:");
                scanf("%s", num);
                strcpy(e.num, num);
                
                printf("请输入高数成绩:");
                scanf("%d", &math_score);
                e.math_score = math_score;
                
                printf("请输入数据结构成绩:");
                scanf("%d", &datastruct_score);
                e.datastruct_score = datastruct_score;
                
                printf("请输入英语成绩:");
                scanf("%d", &English_score);
                e.English_score = English_score;
                
                printf("请输入物理成绩:");
                scanf("%d", &physics_score);
                e.physics_score = physics_score;
                
                printf("请输入想插入的位置(1-%d):\n", L.length + 1);
                scanf("%d", &i);
                
                if (listinsert(&L, i, e)) {
                    printf("输入成功！建议保存刚添加的数据。\n");
                } else {
                    printf("插入失败\n");
                }
                printf("\n");
				break;
            }
            
            case 3:{
                save_binfile(L, "studinfo.dat"); // 写入二进制文件
				free(L.data); 
				printf("\n");
				break; 
			}
			
			case 4: {
                printf("输入想删除学生的学号:");
                scanf("%s", num);
                deleteelem(num);
				break;
            }
            
            case 5:{
                printf("输入待查学生的学号:");
                scanf("%s", num);
				find_by_num(num);
				printf("\n");
				break;
            }
            
			case 6:{
            	printf("输入待查学生的姓名:");
                scanf("%s", name);
                find_by_name(name);
                printf("\n");
				break;
			} 
			
			case 7: {
                printf("输入想修改的学生学号:");
                scanf("%s", num);
				revisestudinfo_by_num(num);
				printf("\n");
				break;
            }
            
            case 8: {
                printf("输入想修改的学生姓名:");
                scanf("%s", name);
				revisestudinfo_by_name(name);
				printf("\n");
				break;
			}
			case 9: {
                sort_by_average();
                printf("\n");
				break;
            }
			
			case 10:{
				printf("退出系统\n");
				free(L.data); 
				return 0;
				break;
			}
            default:
                printf("无效选项，请重新输入\n");
    
    	}	
	}
}
