C语言链表和文件案例,C语言结合文件操作的链表实例

C语言结合文件操作的链表实例

一、 功能

设计一个学生成绩管理系统,要求:

录入学生成绩

查询学生成绩

删除学生成绩

修改学生成绩

二、 ADT接口

/* 初始化一个链表 */

void InitializeList(List * plist);

/* 确定链表是否为空 */

bool ListIsEmpty(const List *plist);

/* 确定链表是否已满 */

bool ListIsFull(const List *plist);

/* 确定链表中的项数 */

unsigned int ListItemCount(const List *plist);

/* 在链表的末尾添加项 */

bool AddItem(Item item, List * plist);

/* 把函数作用于链表中的每一项 */

void Traverse(const List *plist, void(*pfun)(Item item));

/* 释放已分配的内存(如果有的话) */

void EmptyTheList(List * plist);

下面的代码实例需要用到上述部分函数,此处只展示函数名,具体接口函数很容易在网上或书上找到,此例程是套用《C Primer Plus(第六版)》的链表接口源码(程序清单17.3、程序清单17.5)。

三、 使用链表

链表结构

struct grade

{

int code;

int grade;

} Item;

typedef struct node

{

Item item;

struct node * next;

} Node;

typedef Node * List;

链表功能

添加成绩:添加成绩本质上是给链表添加一个项。代码逻辑是根据用户输入初始化一个项然后添加在链表上。

/* 添加成绩 */

void addg(List * plist)

{

Item temp;

if (ListIsFull(plist))

puts(" 成绩已满");

else

{

printf(" 请输入学生编号(数字):");

while (scanf("%d", &temp.code) != 1)

{

while (getchar() != '\n')

continue;

printf(" 请输入正确的学生编号(数字):");

}

while (getchar() != '\n')

continue;

printf(" 请输入学生成绩(数字):");

while (scanf("%d", &temp.grade) != 1)

{

while (getchar() != '\n')

continue;

printf(" 请输入正确的学生成绩(数字):");

}

while (getchar() != '\n')

continue;

if (AddItem(temp, plist) == false)

{

fprintf(stderr, " 系统出错\n");

exit(EXIT_FAILURE);

}

}

puts(" 请按回车键返回菜单界面");

getchar();

system("CLS"); // 清屏

}

成绩查询:成绩查询本质上是在链表中寻找一个项(并且在找到后显示该项相关信息)。代码逻辑是根据用户输入初始化一个项的部分数据域(本例是学生编号),然后遍历链表寻找与其匹配的项,若找到输出该项信息(本例是输出学生成绩)。

/* 成绩查询 */

void findg(List * plist)

{

Item temp;

List p = *plist;

if (ListIsEmpty(plist))

puts(" 没有成绩录入");

else

{

printf(" 请输入学生编号(数字):");

while (scanf("%d", &temp.code) != 1)

{

while (getchar() != '\n')

continue;

printf(" 请输入正确的学生编号(数字):");

}

while (getchar() != '\n')

continue;

while (p)

{

if (p->item.code == temp.code)

{

printf(" 该学生成绩为%d\n", p->item.grade);

puts(" 请按回车键返回菜单界面");

getchar();

system("CLS"); // 清屏

return;

}

if (p->next)

p = p->next;

else

break;

}

printf(" 查无此人\n");

}

puts(" 请按回车键返回菜单界面");

getchar();

system("CLS"); // 清屏

}

成绩修改:成绩修改本质上是在链表中寻找所需项,并且在找到后更改该项信息。代码逻辑是根据用户输入初始化一个项的部分数据域(本例是学生编号),然后遍历链表寻找与其匹配的项,若找到可更改该项信息(本例是更改学生成绩)。

/* 成绩修改 */

void revisiong(List * plist)

{

Item temp;

List p = *plist;

if (ListIsEmpty(plist))

puts(" 没有成绩录入");

else

{

printf(" 请输入学生编号(数字):");

while (scanf("%d", &temp.code) != 1)

{

while (getchar() != '\n')

continue;

printf(" 请输入正确的学生编号(数字):");

}

while (getchar() != '\n')

continue;

while (p)

{

if (p->item.code == temp.code)

{

printf(" 该学生成绩为%d\n", p->item.grade);

printf(" 请输入修改后的成绩(数字):");

while (scanf("%d", &p->item.grade) != 1)

{

while (getchar() != '\n')

continue;

printf(" 请输入正确的学生成绩(数字):");

}

while (getchar() != '\n')

continue;

puts(" 修改完成");

puts(" 请按回车键返回菜单界面");

getchar();

system("CLS"); // 清屏

return;

}

if (p->next)

p = p->next;

else

break;

}

printf(" 查无此人\n");

}

puts(" 请按回车键返回菜单界面");

getchar();

system("CLS"); // 清屏

}

成绩删除:成绩删除本质上是在链表中删除指定项。代码逻辑是根据用户输入初始化一个项的部分数据域(本例是学生编号),然后遍历链表寻找与其匹配的项,若找到可从链表中删除该项。

/* 成绩删除 */

void deleteg(List * plist)

{

Item temp;

List p, q;

p = q = *plist;

if (ListIsEmpty(plist))

puts(" 没有成绩录入");

else

{

printf(" 请输入学生编号(数字):");

while (scanf("%d", &temp.code) != 1)

{

while (getchar() != '\n')

continue;

printf(" 请输入正确的学生编号(数字):");

}

while (getchar() != '\n')

continue;

if (p->item.code == temp.code) // 第一个项为查找目标项时

{

q = p;

p = q->next;

free(q);

puts(" 成功删除");

puts(" 请按回车键返回菜单界面");

getchar();

system("CLS"); // 清屏

return;

}

while (p)

{

if (p->next->item.code == temp.code)

{

q = p->next;

p->next = q->next;

free(q);

puts(" 成功删除");

puts(" 请按回车键返回菜单界面");

getchar();

system("CLS"); // 清屏

return;

}

if (p->next)

p = p->next;

else

break;

}

printf(" 查无此人\n");

}

puts(" 请按回车键返回菜单界面");

getchar();

system("CLS"); // 清屏

}

浏览所有:浏览所有本质上是依次展示链表的项的信息。代码逻辑是运用ADT接口的void Traverse(const List *plist, void(*pfun)(Item item));函数,该函数可以实现对链表的项依次使用函数void(*pfun)(Item item)。

/* 浏览所有 */

void seeg(List * plist)

{

if (ListIsEmpty(plist))

puts(" 没有成绩录入");

else

{

printf(" 学生成绩如下:\n");

Traverse(plist, showg);

}

puts(" 请按回车键返回菜单界面");

getchar();

system("CLS"); // 清屏

}

/* 浏览成绩函数 */

void showg(Item item)

{

printf(" %d: %d\n", item.code, item.grade);

}

代码量多是判断用户输入,真正的链表操作代码量少且接口函数的使用让逻辑非常清晰。此外,为了让用户界面更加美观,此例程用到了清屏函数system("CLS");。

四、 文件操作

为了使该系统使用之后数据得以保留,本例程需要用到文件操作。由于链表的一个项有两个变量,分别存储学生编号和学生成绩,故使用两个文件来分别储存学生编号和学生成绩。假设文件非空,那么程序刚开始运行的时候得把文件的数据读出来给链表。本例程使用最笨的方法:

先计算文件的数据量(此量即之后初始化链表的项数);

把文件内容存到字符串数组中,同时把字符串数组转换为整型数组(因为本例链表的项的变量的类型都是整型);

把数组按顺序赋给链表(此步骤本质上是链表的初始化,即添加项给链表);

链表部分的操作,就是本系统的功能部分:添加成绩、成绩查询、成绩修改、成绩删除、浏览所有等;

链表操作完后把链表的项一个一个重新赋给数组,同时计算链表的项数;

把整型数组转换为字符串数组同时存入文件;

把创建的链表free掉

5、6步骤其实是3、2步骤的逆向操作,此设计对文件为空的情况也适用,前提是文件存在,文件是ANSI的文本文件。值得注意的是,数组和链表赋值部分容易忽略项数或者错误计算项数,这会导致文件存入部分出错从而产生乱码。

FILE *fp1, *fp2;

int n, i;

n = i = 0;

char cr[SLEN];

/* 计算文件的数据量n */

if ((fp1 = fopen("c.txt", "r")) == NULL)

{

fprintf(stdout, " 打开系统失败\n");

exit(EXIT_FAILURE);

}

while (fscanf(fp1, "%s", cr) != EOF)

n++; // 计算文件的数据量a

rewind(fp1);

if (fclose(fp1) != 0)

printf(" 关闭系统失败\n");

/* ***************** */

char c1[SLEN]; int c2[n];

char gs1[SLEN]; int gs2[n];

/* 读取文件 */

if ((fp1 = fopen("c.txt", "r")) == NULL)

{

fprintf(stdout, " 打开系统失败\n");

exit(EXIT_FAILURE);

}

while (fscanf(fp1, "%s", cr) != EOF)

{

sprintf(c1, "%s", cr);

c2[i++] = atoi(c1); // 把文件1中的数据一个一个存到c数组

}

i = 0;

rewind(fp1);

if (fclose(fp1) != 0)

printf(" 关闭系统失败\n");

if ((fp2 = fopen("gs.txt", "r")) == NULL)

{

fprintf(stdout, " 打开系统失败\n");

exit(EXIT_FAILURE);

}

while (fscanf(fp2, "%s", cr) != EOF)

{

sprintf(gs1, "%s", cr);

gs2[i++] = atoi(gs1); // 把文件2中的数据一个一个存到gs数组

}

rewind(fp2);

if (fclose(fp2) != 0)

printf(" 关闭系统失败\n");

/* ******** */

int choice;

List grades;

Item temp;

/* 更新链表 */

InitializeList(&grades);

if (ListIsFull(&grades))

{

fprintf(stderr, " 系统出错\n");

exit(1);

}

for (i = 0; i < n; i++)

{

temp.code = c2[i];

temp.grade = gs2[i];

if (AddItem(temp, &grades) == false)

{

fprintf(stderr, " 系统出错\n");

exit(EXIT_FAILURE);

}

system("CLS"); // 清屏

}

/* ******** */

/* 系统菜单 */

puts("= = = = = = = = = = = = = = = = = = =");

puts("| 学生信息管理系统 |");

puts("= = = = = = = = = = = = = = = = = = =\n");

while ((choice = menu()) != 0)

{

switch (choice)

{

case 1: addg(&grades); // 添加成绩

break;

case 2: findg(&grades); // 成绩查询

break;

case 3: revisiong(&grades); // 成绩修改

break;

case 4: deleteg(&grades); // 成绩删除

break;

case 5: seeg(&grades); // 浏览所有

break;

default: puts(" 系统出错,请重新输入:");

}

}

/* ******** */

/* 更新数组 */

List pre = grades;

n = i = 0;

while (pre)

{

c2[i] = pre->item.code;

gs2[i++] = pre->item.grade;

n = i; // 计算链表的数据量n

pre = pre->next;

}

pre = grades;

/* ******** */

/* 存入文件 */

if ((fp1 = fopen("c.txt", "w")) == NULL)

{

fprintf(stdout, " 打开系统失败\n");

exit(EXIT_FAILURE);

}

for (i = 0; i < n; i++)

{

sprintf(c1, "%d", c2[i]);

fprintf(fp2, "%s\n", c1); // 把c数组的数据一个一个存到文件1中(更新文件)

}

rewind(fp1);

if (fclose(fp1) != 0)

printf(" 关闭系统失败\n");

if ((fp2 = fopen("gs.txt", "w")) == NULL)

{

fprintf(stdout, " 打开系统失败\n");

exit(EXIT_FAILURE);

}

for (i = 0; i < n; i++)

{

sprintf(gs1, "%d", gs2[i]);

fprintf(fp2, "%s\n", gs1); // 把gs数组的数据一个一个存到文件2中(更新文件)

}

rewind(fp2);

if (fclose(fp2) != 0)

printf(" 关闭系统失败\n");

/* ******** */

EmptyTheList(&grades);

puts(" 成功退出系统");

你可能感兴趣的:(C语言链表和文件案例)