学生成绩管理(二进制文件读写)
【问题描述】
在以下问题中,定义学生的结构体student,录入学生信息,并存入到二进制文件grade.bin中,要求存入的是按照总成绩从大到小的顺序存储,数据可以分多次录入,每次录入前,都将显示文件中已有的数据,其具体形式如下:
No: 3
Name: Hong
Sex: F
Age: 9
Scores: 123.000000 531241.000000
No: 2
Name: Zhang
Sex: F
Age: 98
Scores: 123.000000 434.000000
No: 1
Name: Wang
Sex: M
Age: 32
Scores: 12.000000 54.000000
Input continue(y/n)?
在提示Input continue(y/n)?下输入’y’,则可以继续输入,否则将退出。继续输入的内容将与现有的数据进行重新按总分从大到小的顺序排列后重新写入文件grade.bin中。
【输入形式】
Input continue(y/n)?y
No: 15
Name: LiuYing
Sex(M/F): M
Age: 25
Chinese score: 100
Computer score: 85
Input continue(y/n)?y
No: 16
Name: WangLiLi
Sex(M/F): F
Age: 23
Chinese score: 95.5
Computer score: 68.8
Input continue(y/n)?n
【输出形式】文件
调试时的测试文件grade.bin:下载链接为http://gofile.me/4tv7L/5O3fI22ZP,(下载文件``竟不存在,这究竟是天性的沦丧还是人性的泯灭还是我太垃圾了? )请将这个文件下载到与源代码相同的文件夹下。
#include <iostream>
using namespace std;
/*
定义结构体student用于表示学生的基本信息,采用链表结构
*/
struct student
{
int no; // 学号
char name[20], sex; // 姓名以及性别, 请思考如果将name[20]修改为*name,结果将会怎样,还适合本题的处理方式吗?
int age; // 年龄
float chinese, computer; // 表示两门课程的成绩
student *next;
bool operator<(const student &another) const // 重载运算符 < (小于),用于结构体对象之间的比较,在LinkSort中比较链表的两个节点大小
{
return
(chinese + computer) < (another.chinese + another.computer)
;
}
};
student* ReadFile(FILE *fp, student *head, int &n); // 从二进制文件(文件指针为fp)中将所有数据读入,存储于以head为头指针的链表中
void display(student *head); // 用于显示链表的内容
void WriteFile(FILE *fp, student *head); // 将以head为头指针的链表中的数据写入二进制文件(文件指针为fp)
student* input(student *head, int &n); // 输入新的数据,将新数据连接到链表末尾
student* LinkSort(student *head, const int &n); // 实现对链表的排序,按总成绩从大到小排列,冒泡排序
void swapNode(student *s, student *q); // 实现对链表节点的数据交换,用于排序, 在函数LinkSort中调用
int main()
{
int n = 0; // 定义变量n,用于存储学生人数
student *head = NULL; // 链表头指针
FILE *fp;
fp = fopen("grade.bin",
"rb+"
); // 打开二进制文件,用于读写以及添加记录
head = ReadFile(
fp, head, n
);
display(head);
fclose(fp);
head = input(head, n);
head = LinkSort(head, n);
fp = fopen("grade.bin", "wb");
WriteFile(fp, head);
fclose(fp);
return 0;
}
student* ReadFile(FILE* fp, student* head, int &n) //本函数将调用fread函数读数据块,这个函数非常有用
{
student *q = head;
if (head)
while(q->next) q = q->next;
while(1)
{
student *p = new student; // 用于存储读入的数据块
if (!fread(
p,sizeof(student),1,fp
)) // 从文件fp中读入大小为sizeof(student)的数据块,保存于内存缓冲区p之中, fread将返回读入的字节数,如果为0,则结束读文件
{
delete p;
break;
}
p->next = NULL;
if (!head)
head = p;
else
q->next = p;
q = p;
n++;
}
return head;
}
void WriteFile(
FILE *fp, student *head
) //本函数将调用fwrite函数写数据块,这个函数非常有用
{
while(head)
{
fwrite(
head,sizeof(student),1,fp
); // 将缓冲区head中的内容(数据块)写入文件中
head = head->next;
}
}
void display(student* head)
{
student *p = head;
while(p)
{
printf("No: ");
cout << &p->no << endl;
printf("Name: ");
cout << p->name << endl;
printf("Sex(M/F): ");
cout << p->sex << endl;
printf("Age: ");
cout << &p->age<<endl;
printf("Chinese score: ");
cout << &p->chinese << endl;
printf("Computer score: ");
cout<< &p->computer<<endl;
p=p->next;// 输出链表节点内容
}
}
student* input(student* head, int &n)
{
student *q = head;
if (head)
while(q->next) q = q->next;
while(1)
{
printf("Input continue(y/n)?");
if (getchar() != 'y')
break;
student *p = new student;
printf("No: ");
scanf("%d", &p->no);
getchar();
printf("Name: ");
gets(p->name);
printf("Sex(M/F): ");
p->sex = getchar();
printf("Age: ");
scanf("%d", &p->age);
printf("Chinese score: ");
scanf("%f", &p->chinese);
printf("Computer score: ");
scanf("%f", &p->computer);
p->next = NULL;
getchar();
if (!head)
head = p;
else
q->next = p;
q = p;
n++;
}
return head;
}
student* LinkSort(student* head, const int &n)
{
student *p;
for(int i = 1; i <= n - 1; i++)
{
p = head;
for(int j = 1; j <= n - i; j++)
{
if (*p < *p->next)
{
swapNode(p , p->next);
// 交换节点值
}
p = p->next;
}
}
return head;
}
void swapNode(student* s, student* q)
{
student *snext = s->next, *qnext = q->next, tmp;
tmp = *s;
*s = *q;
*q = tmp;
s->next = snext;
q->next = qnext;
}
用到FILE结构体中的函数:
1.FILE *fopen(const char *filename,const char *mode)
例如:fp = fopen(“file.txt”, “w+”);
2.size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
- void *ptr:指向用于储存读取内容的内存块。void是无类型指针,可以指向任意类型的数据。
- size_t size:读取的内存块的大小,以字节为单位。可以用sizeof() 获得。
- size_t nmemb:读取的内存块的次数。
- FILE *stream:指向要读取的文件(输入流)的指针。
3.size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream)
与fread()类似。
注:fwrite和fread的返回值不是字节数,而是实际读取的规定大小代码块的个数。可能与函数调用的第三个参数不同。
…
…
…
…
…
…
…
…
…
…
(咸鱼+菜鸟的所有文章都用于方便自己总结一些易错内容和知识点,如有错漏,纯属应当(滑稽逃跑))