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

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

一、 功能

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

  1. 录入学生成绩
  2. 查询学生成绩
  3. 删除学生成绩
  4. 修改学生成绩

二、 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;
链表功能
  1. 添加成绩:添加成绩本质上是给链表添加一个项。代码逻辑是根据用户输入初始化一个项然后添加在链表上。
/* 添加成绩 */
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");  // 清屏
}
  1. 成绩查询:成绩查询本质上是在链表中寻找一个项(并且在找到后显示该项相关信息)。代码逻辑是根据用户输入初始化一个项的部分数据域(本例是学生编号),然后遍历链表寻找与其匹配的项,若找到输出该项信息(本例是输出学生成绩)。
/* 成绩查询 */
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");  // 清屏
}
  1. 成绩修改:成绩修改本质上是在链表中寻找所需项,并且在找到后更改该项信息。代码逻辑是根据用户输入初始化一个项的部分数据域(本例是学生编号),然后遍历链表寻找与其匹配的项,若找到可更改该项信息(本例是更改学生成绩)。
/* 成绩修改 */
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");  // 清屏
}
  1. 成绩删除:成绩删除本质上是在链表中删除指定项。代码逻辑是根据用户输入初始化一个项的部分数据域(本例是学生编号),然后遍历链表寻找与其匹配的项,若找到可从链表中删除该项。
/* 成绩删除 */
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");  // 清屏
}
  1. 浏览所有:浏览所有本质上是依次展示链表的项的信息。代码逻辑是运用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");

四、 文件操作

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

  1. 先计算文件的数据量(此量即之后初始化链表的项数);
  2. 把文件内容存到字符串数组中,同时把字符串数组转换为整型数组(因为本例链表的项的变量的类型都是整型);
  3. 把数组按顺序赋给链表(此步骤本质上是链表的初始化,即添加项给链表);
  4. 链表部分的操作,就是本系统的功能部分:添加成绩、成绩查询、成绩修改、成绩删除、浏览所有等;
  5. 链表操作完后把链表的项一个一个重新赋给数组,同时计算链表的项数;
  6. 把整型数组转换为字符串数组同时存入文件;
  7. 把创建的链表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("              成功退出系统");
  • 8
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值