学生信息管理系统——输入检查,链表创建,结点的插入、修改、删除,文件的创建、读取、屏显(学习一个月来,第一次写超过400句的代码(好吧,空行也挺多的))

前言

初学C语言,请大佬多提宝贵意见,感激不尽。欢迎各位初学者前来交流。

/*
--------------------------------------------------------------------------
学生信息管理系统:
				 1.链表的创建
				 2.结构体中的成员查找
				 3.链表中的元素查找
				 4.链表中的结点删除、修改、插入。
				 5.文件的创建。
				 6.文件的读取。
				 7.文件的屏幕显示。
				 8.输入的安全性(防止输入错误导致软件奔溃或者数据删除)
--------------------------------------------------------------------------
总结:
	 1.花了5个小时才写完。
	 2.链表的冒泡排序还没有思考怎么操作,不想再自己思考了,直接看前辈们的吧。
	 3.不知道自己的 C 语言现在有没有入门,缺乏交流。
	 4.利用while,可以实现输入错误重新输入的目的。
--------------------------------------------------------------------------

*/

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

typedef struct Student 
{
	char st_ID[20];
	char st_name[20]; 
	float st_score;
	struct Student *pNext;

} STU;

STU head;
STU *p, *tail;
char filename1[20];

void InputStudent();
void OutputStudent();
void InsertStudent();
void DeleteStudent();
void RepairStudent();
void SearchStudent();
void ScalStudent();
void file();
void exchange();


int main(void)
{
	int n;
	head.pNext = NULL;
	
	while(1)
	{
		printf("1. 创建学生成绩链表\n");
		printf("2. 显示所有学生成绩\n");
		printf("3. 在指定位置插入成绩信息\n");
		printf("4. 根据学号删除成绩信息\n");
		printf("5. 根据排列的“序号”修改成绩\n");
		printf("6. 根据学号查询成绩\n");
		printf("7. 计算平均成绩\n");
		printf("8. 退出\n");
		printf("9. 利用生成的文件进行读取和输出完成相应动作\n");

		printf("\n 请输入你需要的功能选项\n");
		scanf("%d", &n);
		switch(n)
		{
		case 1: InputStudent(); break;
		case 2: OutputStudent(); break;
		case 3: InsertStudent(); break;
		case 4: DeleteStudent(); break;
		case 5: RepairStudent(); break;
		case 6: SearchStudent(); break;
		case 7: ScalStudent(); break;
		case 8: exit(0); break;
		case 9: file(); break;
		
			
		}

	}
	return 0;
}

void InputStudent()
{
	int i, len;
	FILE *fp;
	if(head.pNext != NULL)
	{
		printf("表格已存在\n");
		return ;
	}

	tail = &head;
	printf("请输入有多少个学生 len = ");
	scanf("%d", &len);
	flushall();				//!!!一开始出错了。!!!若是没有此句,程序会报错,原因是回车给了gets,文件名为'\0'
	printf("请输入需要创建的文件名称 : ");
	gets(filename1);
	fp = fopen(filename1, "w");

	for(i=0; i<len; i++)
	{
		p = (STU *)malloc(sizeof(STU));
		p->pNext = NULL;			//链表的创建过程不再赘述,若想学习,看书,或者看我之前的博客。

		if(NULL == p)
		{
			printf("链表创建失败,空间不足\n");
			exit(0);			
		}

		printf("请输入第%d个学生的信息: ", i+1);
		scanf("%s%s%f", p->st_ID, p->st_name, &p->st_score);	//输入链表内容
		fprintf(fp, "序号%-3d%-10s%-10s%-5f", i+1,p->st_ID, p->st_name, p->st_score);	//输入文本内容。
		fputc('\n', fp);
		tail->pNext = p;
		tail = p;
	}
	fclose(fp);
	
	return ;
}

void OutputStudent()
{
	if(head.pNext == NULL)
	{
		printf("表格尚未创建\n");
		return ;
	}
	
	p = head.pNext;
	while(p != NULL)
	{
		printf("序号:%-3d%-10s%-10s%-5f\n", i, p->st_ID, p->st_name, p->st_score);
		p = p->pNext;
	}

	return ;
}
/*
// 用读取文件表达。与上文中功能一样,所以注释掉了
void OutputStudent()
{
	FILE *fp;
	STU *p_1;
	char ch[4];
	int i;

//	if(filename1 == NULL)
//	{
//		printf("文件尚未创建\n");	//如何判断文件尚未建立?此句有逻辑错误,暂时未完成。
//		return ;		
//	}
	fp  = fopen(filename1, "r");
	if(NULL == fp)
	{
		printf("文件尚未创建\n");
		return ;
	}

	while(!feof(fp))
	{
		fgets(ch, 4, fp);
		printf("%s", ch);
	}

	return ;
}
*/
void InsertStudent()
{
	STU *sqr;
	char ch[6];

	sqr = (STU *)malloc(sizeof(STU));
	if(	NULL == sqr)
	{
		printf("插入失败\n");
		return ;
	}
//	printf("请输入查询的ID:");
//	scanf("%s", sqr->st_ID);

	if(NULL == head.pNext)
	{
		printf("表格尚未建立");
		free(sqr);
		return;
	}

	int i=0;
	while(1)		//查错代码,后面一样,不再赘述
	{
		p = &head;  //p指向了头结点
		printf("请输入查询的ID:");
		scanf("%s", sqr->st_ID);
		flushall();

		while(p->pNext != NULL)	//头结点后面的那个结点(首结点)存在。
		{
			if((strcmp(p->pNext->st_ID, sqr->st_ID)) == 0)	//主要还是判断P指针后面的那个结点的成员是否符合要求,若是符合,则将p->pNext所指向的那个结点(p后面的那个结点)的地址付给将要插入的结点sqr->pNext中,再将p->pNext = sqr,这样才能建立起链表,不会中断。这个逻辑一定要搞明白。
			{
				printf("请输入插入的信息:");
				scanf("%s%s%f", sqr->st_ID, sqr->st_name, &sqr->st_score);
				sqr->pNext = p->pNext;
				p->pNext = sqr;
		//		printf("请输入插入的信息:");
				
				return ;
			}
			
			p = p->pNext;
		}
		printf("ID不存在,请重新输入,若要退出,请输入1");	//查错代码。
		scanf("%d", &i);
		if(i == 1)
			return ;
	}

	return ;
}

void DeleteStudent()
{
	char ch[20];
	
	STU *str;
	int i;

	while(1)
	{
		p = &head;
		printf("请输入需要删除的ID: ");
		scanf("%s", ch);
		while(p->pNext != NULL)
		{
			if(strcmp(p->pNext->st_ID , ch) == 0)	//同上面的函数一样的道理,如果直接删除p,则p结点的上一个结点无法找到,所以只能删除p的下一个结点,让p中的指针域能存放p 的下 下个结点的地址。
			{
				str = p->pNext;
				p->pNext = str->pNext;
				free(str);
				return ;
			}
			p = p->pNext;
		}
		printf("ID不存在, 返回上次层按1, 继续按其他:\n");
		scanf("%d", &i);
		if(i == 1)
			return ;
	}




	return ;
}

void RepairStudent()
{
	int len, i;


	if(head.pNext == NULL)
	{
		printf("表格不存在\n");
		return ;
	}

	printf("请输入需要修改的序列号: ");
	scanf("%d", &len);
	while(len <1)
	{
		printf("输入的值小于1,请重新输入大于0的值:");
		scanf("%d", &len);
	}

	int j=0, fla = 1;
	while(fla)
	{
		p = &head;
		fla = 0;	//若是一次就输入正确,则不需要再重新输入,所以while(fla)将不再执行
		for(i=1; i<=len; i++)
		{
			p = p->pNext;
			if((p->pNext == NULL) && (i<len))
			{
				fla = 1;	//若fla为1 ,则说明输入的序号有误,需要重新进行一次扫描,从头开始,而while(fla)就是起这个功能。
				j = 1;
				printf("输入的值过大,请输入小于%d的值 : ", i+1);
				scanf("%d", &len);
				while(len<1 || len>i)
				{
					printf("输入的值太小或太大,请看提示重新输入 : ");
					scanf("%d", &len);
				}
			}
			if(j == 1)	//若是j 为 1 ,则说明输入的序号合法,跳出for循环,执行删除语句。为什么要有 j == 1?答:若是没有j == 1 判断语句,则在正常的情况下(假如len = 2,而实际的数据组有5组(删除的那一行存在)),则第一次执行for语句就跳出了,指针P无法移动到需要删除的位置。
				break;
			
		}
	}

	printf("请输入需要插入的值 : ");
	scanf("%s%s%f", p->st_ID, p->st_name, &p->st_score);

	return ;
}

void SearchStudent()
{
	char ch[20];

	printf("请输入需要显示的ID : ");
	while(1)
	{
		scanf("%s", ch);
		p = head.pNext;
		while(p != NULL)
		{
			if(strcmp(p->st_ID, ch) == 0)
			{
				printf("%-6s%-6s%-3f\n", p->st_ID, p->st_name, p->st_score);
				return ;
			}
			p = p->pNext;
		}
		printf("输入的id有误,请重新输入: ");
	}
	return ;
}

void ScalStudent()
{	
	float avg, sum = 0;
	int i=0;

	p = head.pNext;
	if(head.pNext == NULL)
	{
		printf("表格不存在\n");
		return ;
	}
	while(p !=NULL)
	{
		sum += p->st_score;
		i++;
		p = p->pNext;
	}
	printf("sum = %f, i = %d\n", sum, i);

	avg = sum/i;
	printf("%f\n", avg);
	return ;
}

//利用文件,将指定的值输出生成新的链表
// 注意,此文件中,文件指针p_1指向的文件filename格式有误,前面还有一个字符串,需要重新定义一个结构体,实在没精力更改。若想试验,自己把下文中的fp_1 = fopen(filename1, "r"); 改为:fp_1 = fopen("1.txt", "r");再重新手动建立一个1.txt的文档即可。
void file()
{

	int n;
	STU head_1;
	STU *tail_1, *p_1;

	head_1.pNext = NULL;
	tail_1 = &head_1;

	FILE *fp_1;
	fp_1 = fopen(filename1, "r");
	if(fp_1 == NULL)
	{
		printf("文件创建失败\n");
		return ;
	}

	while(!feof(fp_1))
	{
		p_1 = (STU *)malloc(sizeof(STU));
		p_1->pNext = NULL;
		if(NULL == p_1)
		{
			printf("创建失败\n");
			return ;
		}
		n = fscanf(fp_1, "%s%s%f", p_1->st_ID, p_1->st_name, &p_1->st_score); //此句用于检测fscanf读取的是不是3个有效数值。
		if(n != 3)
		{
			free(p_1);	//好习惯,记得把不用的释放。
			break;
		}
		tail_1->pNext = p_1;
		tail_1 = p_1;	
	}
// 输出:
	printf("以下为工程输出\n");
	p_1 = head_1.pNext;
	while(p_1 != NULL)
	{
		printf("%-5s, %-5s, %-3f\n", p_1->st_ID, p_1->st_name, p_1->st_score );
		p_1 = p_1->pNext;
	
	}
	
	return ;
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值