[例2]学生信息管理程序(数据结构C语言版 李云清)

[例2]学生信息管理程序(数据结构C语言版 李云清)

(1)问题描述:

学生信息包括:学号、姓名、年龄、性别、出生年月、地址、电话和Email等, 试设计一个学生信息管理程序. 实现学生信息的电于化管理. 要求: 使用文件方式存储数据, 采用链表组织学生数据.

(2)基本要求 系统应具有以下基本功能

1.系统以菜单方式工作
2.学生信息录入功能(学生信息用文件保存) - 输入
3.学生信息浏览功能 - 输出
4.学生信息查询功能 - 按学号查询、按姓名杳询
5.学生信息的删除与修改
6.学生信息的排序(按学号, 按年龄)

(3)算法分析

本课程设计主要训练学生应用链表存储和管理信息的综合能力, 涉及链表的删除、查找、插入和排序等基本算法.

写在前面:

1.ver4: 2019/11/9 更新

  1. 可以从github克隆了 GitHub地址
  2. 修复了创建数据文件(make)或删除数据文件(del)后, 查看数据文件(dir)时文件个数显示不正常的问题 详情: 修改dir()函数检查到无数据文件时的返回值, 从-1改为0, case 14: remove删除后调用dir重新统计count的值

2. ver3: 2019/10/27更新

  1. 对switch语句进行了维护, 可以通过命令的形式操作菜单, 并增加了help帮助选项
  2. 对链表中学号重复的情况进行了更改, 现在不允许学号重复, 但仍允许重名
  3. 允许用户建立 删除 查看已有数据库, 并能在不退出应用的情况下访问多个数据库, 运行前不需要手动建立数据文件

3.ver2: 2019/10/11更新(原创建于2019/10/10)

  1. 合并了main.c与funcBase.c文件
  2. 头文件重写使得更符合标准
  3. 修复了目标创建错误无法打开的问题(详细:将char input_fileName[MaxFileName] ="manage_information_students\\StuData.dat"改为char input_fileName[MaxFileName] = "StuData.dat")
  4. 增加了gcc命令使用说明

4.gcc使用说明:

  1. 将命令行转到文件目录下, 附上文档 cmd命令
  2. 使用命令gcc dataBase.c main.c -o main编译生成main.exe(call.h不用编译),附上文档 Windows命令行中使用gcc/g++同时编译多个文件
call.h 文件
/*
 * @Description: 
 * @Author: LiuKai
 * @Date: 2019-10-08 20:57:06
 * @Version:3
 * @LastEditTime: 2019-10-27 15:11:52
 * @LastEditors: Liu Kai
 */
#ifndef __CALL_H__
#define __CALL_H__
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include "regex.h"
#include <io.h>
#include "dataBase.c"
 

// 前向声明块
void pmenu();
void make(char *input_fileName);
int dir(char* path);
node_stu* load(node_stu* head, char* fileName);
void save(node_stu* head, char* fileName);
node_stu* init();
node_stu* insert(node_stu* head);
void prints(node_stu* head);
node_stu* findnum(node_stu* head, char* str_num);
node_stu* findname(node_stu* head, char* str_name);
node_stu* modify_num(node_stu* head, char* num, bool modify);
node_stu* modify_name(node_stu* head, char* name, bool modify);
int override_strcmp(char* str1, char* str2);
node_stu* delets(node_stu* head);

#endif

dataBase.c 文件
/*
 * @Description: 
 * @Author: LiuKai
 * @Date: 2019-10-08 20:59:46
 * @Version:3
 * @LastEditTime: 2019-10-27 15:12:05
 * @LastEditors: Liu Kai
 */
#define STR_SIZE 100
#define INFO_SIZE 10
#define MAXSIZE 100
#define MaxFileName 100
#define SELATION 16
typedef char datatype;
typedef struct node {
    datatype num_stu[STR_SIZE];
    datatype name[STR_SIZE];
    datatype birth[STR_SIZE];
    datatype age[STR_SIZE];
    datatype sex[STR_SIZE];
    datatype addr[STR_SIZE];
    datatype email[STR_SIZE];
    datatype tele[STR_SIZE];
    struct node* next;
} node_stu;

char selarr[SELATION][STR_SIZE] = {
    "insert",    "print",      "findnum", "findname", "delnum", "delname",
    "modifynum", "modifyname", "exit",    "clear",    "help",   "dir",
    "open",      "make",       "del",     "close"};

main.c 文件
/*
 * @Description: 学生信息管理程序
 * @Author: LiuKai
 * @Date: 2019-10-08 20:56:51
 * @Version:4
 * @LastEditTime: 2019-11-09 08:57:44
 * @LastEditors: Liu Kai
 */
#include "call.h"
extern int errno;
char FileName[MaxFileName][MaxFileName];
int main(int args, char* argv[]) {
    // system("chcp 936");
    // system("cls");
    FILE* fp;
    node_stu* StuNode = NULL;
    char path[MaxFileName];                    // 当前工作路径
    char input_fileName[MaxFileName] = "\0";   // 输入文件
    char output_fileName[MaxFileName] = "\0";  // 输出文件
    bool exitFlag = false;
    bool filexist = false;
    bool tocls = false;
    int sel;
    char strsel[30];  // switch选择指示
    char num[STR_SIZE];
    char name[STR_SIZE];

    // char* _getcwd(char* buffer, int maxlen);
    _getcwd(path, MaxFileName);
    printf("当前工作目录: %s\n", path);
    // 为dir添加查找筛选
    strcat(path, "\\*.dat");
    int count = dir(path);

    printf("help获取帮助\n");
    while (true) {
        scanf("%s", strsel);
        for (sel = 0; sel < SELATION; sel++) {
            if (!strcmp(strsel, selarr[sel])) {
                break;
            }
        }
        switch (sel) {
            case 0:
                if (StuNode) {
                    StuNode = insert(StuNode);
                } else {
                    printf("当前无打开的数据文件\n");
                }
                break;
            case 1:
                if (StuNode) {
                    prints(StuNode);
                } else {
                    printf("当前无打开的数据文件\n");
                }
                break;
            case 2:
                if (StuNode)
                    scanf("%s", num);
                findnum(StuNode, num);
                break;
            case 3:
                if (StuNode) {
                    scanf("%s", name);
                    findname(StuNode, name);
                } else {
                    printf("当前无打开的数据文件\n");
                }
                break;
            case 4:
                if (StuNode) {
                    scanf("%s", num);
                    StuNode = modify_num(StuNode, num, false);
                } else {
                    printf("当前无打开的数据文件\n");
                }
                break;
            case 5:
                if (StuNode) {
                    scanf("%s", name);
                    StuNode = modify_name(StuNode, name, false);
                } else {
                    printf("当前无打开的数据文件\n");
                }
                break;
            case 6:
                if (StuNode) {
                    scanf("%s", num);
                    StuNode = modify_name(StuNode, num, true);
                } else {
                    printf("当前无打开的数据文件\n");
                }
                break;
            case 7:
                if (StuNode) {
                    scanf("%s", name);
                    StuNode = modify_name(StuNode, name, true);
                } else {
                    printf("当前无打开的数据文件\n");
                }
                break;
            case 8:
                exitFlag = true;
                break;
            case 9:
                printf("初始化数据文件(所有数据将被清除)\n");
                printf("继续?(输入y或Y继续)\n");
                char consel[1];
                scanf("%s", consel);
                if (consel[0] == 'Y' || consel[0] == 'y') {
                    StuNode = init();
                    exitFlag = true;
                    printf("数据文件初始化完成\n");
                } else {
                    printf("操作已放弃\n");
                }
                break;
            case 10:
                pmenu();
                break;
            case 11:
                // dir
                printf("共有%d个数据文件\n", count);
                for (int i = 0; i < count; i++) {
                    printf("%s\n", FileName[i]);
                }
                break;
            case 12:
                // open
                if (StuNode) {
                    printf("正在保存上一个数据文件...\n");
                    save(StuNode, output_fileName);
                    printf("保存完成\n");
                }
                while (true) {
                    // printf("输入数据文件名以操作:\n");
                    scanf("%s", input_fileName);
                    for (int i = 0; i < count; i++) {
                        if (!strcmp(input_fileName, FileName[i])) {
                            filexist = true;
                        }
                    }
                    if (filexist) {
                        break;
                    } else {
                        printf("输入错误或文件不存在\n");
                    }
                }
                // 对同一个文件进行操作
                strcpy(output_fileName, input_fileName);
                printf("初始化内存, 向内存加载数据...\n");
                StuNode = init();
                StuNode = load(StuNode, input_fileName);
                printf("数据加载完成\n");
                break;
            case 13:
                // make
                scanf("%s", input_fileName);
                make(input_fileName);
                count = dir(path);
                break;
            case 14:
                // del
                scanf("%s", input_fileName);
                if (!remove(input_fileName)) {
                    printf("删除成功\n");
                    count = dir(path);
                } else {
                    fprintf(stderr, "%s\n", strerror(errno));
                }
                // 如果删除的文件和当前打开的文件是同一个文件则释放StuNode
                if (!strcmp(input_fileName, output_fileName)) {
                    StuNode = delets(StuNode);
                }
                StuNode = NULL;
                break;
            case 15:
                // close
                if (StuNode) {
                    printf("正在保存数据文件...\n");
                    save(StuNode, output_fileName);
                    printf("保存完成\n");
                } else {
                    printf("无需要保存的内容\n");
                }
                break;
            default:
                printf("命令不存在\n");
                break;
        }
        if (exitFlag) {
            break;
        }
    }
    printf("正在将数据写入数据库...\n");
    if (StuNode) {
        save(StuNode, output_fileName);
        printf("数据写入完成,程序即将关闭\n");
    } else {
        printf("无需要保存的内容\n");
    }
    Sleep(2000);
    return 0;
}

void pmenu() {
    printf(
        "dir            查看数据库\n"
        "open           打开数据文件(格式:open [filename])\n"
        "make           建立新数据文件(格式:make [filename])\n"
        "del            删除数据文件(格式:del [filename])\n"
        "close          保存并关闭当前数据文件\n"
        "insert         插入一个数据\n"
        "print          显示所有成员\n"
        "clear          清空数据文件\n"
        "findnum        查询学号\n"
        "findname       查询姓名\n"
        "delnum         以学号为依据查找并删除\n"
        "delname        以姓名为依据查找并删除\n"
        "modifynum      以学号为依据查找并修改\n"
        "modifyname     以姓名为依据查找并修改\n"
        "exit           退出\n"
        "help           帮助\n");
}

/**
 * @description: 建立新数据文件
 * @param {type} 
 * @return: 
 */
void make(char* input_fileName) {
    FILE* fp;

    fp = fopen(input_fileName, "wb");
    if (!fp) {
        fprintf(stderr, "%s\n", strerror(errno));
    }
    fclose(fp);
}

/**
 * @description: 在path目录下查找.dat文件
 * @param {type: char*} path
 * @return: void
 */
int dir(char* path) {
    long hFile = 0;
    struct _finddata_t fileInfo;

    int count = 0;
    // \\* 代表要遍历所有的类型, \\*.dat表示遍历dat类型文件
    if ((hFile = _findfirst(path, &fileInfo)) == -1) {
        // printf("暂无数据文件");
        return 0;
    }
    do {
        // printf("%s\n", fileInfo.name);
        strcpy(FileName[count], fileInfo.name);
        // (fileInfo.attrib & _A_SUBDIR ? "[folder]" : "[file]")
        // 判断文件的属性是文件夹还是文件
        count++;
    } while (_findnext(hFile, &fileInfo) == 0);
    _findclose(hFile);
    return count;
}

/**
 * @description: 从.dat数据库加载数据到链表
 * @param {type: node_stu*}  head
 * @param {type: char*}  fileName
 * @return: head
 */
node_stu* load(node_stu* head, char* fileName) {
    FILE* input;
    node_stu *pre, *p;
    input = fopen(fileName, "rb");
    if (!input) {
        fprintf(stderr, "%s\n", strerror(errno));
        return NULL;
    }
    pre = head;

    p = (node_stu*)malloc(sizeof(node_stu));
    while (fread(p, sizeof(struct node), 1, input)) {
        p->next = pre->next;
        pre->next = p;
        pre = p;
        p = (node_stu*)malloc(sizeof(node_stu));
    }
    free(p);
    fclose(input);
    return head;
}

/**
 * @description: 将数据库写入.dat文件
 * @param {type: node_stu*}  head
 * @param {type: char*}  fileName
 * @return: void
 */
void save(node_stu* head, char* fileName) {
    FILE* output;
    node_stu* p;
    output = fopen(fileName, "wb");
    if (!output) {
        fprintf(stderr, "%s\n", strerror(errno));
        return;
    }
    p = head;
    p = p->next;
    if (!p) {
        printf("Empty\n");
        return;
    }
    while (p) {
        fwrite(p, sizeof(struct node), 1, output);
        int a = ferror(output);
        p = p->next;
    }
    free(p);
    fclose(output);
}

/**
 * @description: 初始化链表
 * @param {type} void
 * @return: void
 */
node_stu* init() {
    node_stu* head;
    head = (node_stu*)malloc(sizeof(node_stu));
    head->next = NULL;
    return head;
}

/**
 * @description:
 * 链表有序(从小到大)插入(插入时先根据学号排序,学号相同时以年龄排序)
 * @param {type: node_stu*} head
 * @return:head
 */
node_stu* insert(node_stu* head) {
    node_stu *p, *pre, *Lastpos;
    pre = head;
    p = (node_stu*)malloc(sizeof(node_stu));
    // 学号不允许重复
    while (true) {
        printf("输入学号 姓名 年龄 性别 出生年月 地址 E-mail 电话\n");
        scanf("%s %s %s %s %s %s %s %s", p->num_stu, p->name, p->age, p->sex,
              p->birth, p->addr, p->email, p->tele);
        if (findnum(head, p->num_stu)) {
            printf("此学号已存在\n");
        } else {
            break;
        }
    }
    //只有一个头结点
    if (!pre->next) {
        p->next = pre->next;
        pre->next = p;
        return head;
    }
    // 从小到大排序
    Lastpos = pre;
    pre = pre->next;
    while (pre && override_strcmp(pre->num_stu, p->num_stu) < 0) {
        Lastpos = pre;
        pre = pre->next;
    }
    // 在头结点插入
    /* if (head->next == Lastpos) {
        p->next = Lastpos->next;
        Lastpos->next = p;
        return head;
    } */
    // bool premove = false;
    if (pre && (override_strcmp(pre->age, p->age) <= 0)) {
        while (pre && override_strcmp(pre->age, p->age) <= 0 &&
               !override_strcmp(pre->num_stu, p->num_stu)) {
            Lastpos = pre;
            pre = pre->next;
            // premove = true;
        }
    }  // else {
    // premove = true;
    pre = Lastpos;
    p->next = pre->next;
    pre->next = p;
    return head;
}

/**
 * @description: 结点连续输出
 * @param {type: node_stu*} head
 * @return:void
 */
void prints(node_stu* head) {
    node_stu* p;
    p = head;
    p = p->next;
    if (!p) {
        printf("Empty\n");
        return;
    }
    while (p) {
        printf("std:%s %s %s %s %s %s %s %s\n", p->num_stu, p->name, p->age,
               p->sex, p->birth, p->addr, p->email, p->tele);
        p = p->next;
    }
    free(p);
}

/**
 * @description: 单节点输出(弃用)
 * @param {type}
 * @return:
 */
/* void print(node_stu* head) {
    node_stu* p;
    p = head;
    p = p->next;
    if (!p) {
        printf("Empty\n");
        return;
    }
    while (p) {
        printf("std:%s %s %s %s %s %s %s %s\n", p->num_stu, p->name, p->age,
               p->sex, p->birth, p->addr, p->email, p->tele);
        p = p->next;
    }
    free(p);
}*/

/**
 * @description: 按学号查询(学号相同时激发姓名查询)
 * 延迟绑定,这里findnum()的返回值是找到的上一个结点,但是输出不延迟
 * @param {type :node_stu*} head
 * @param {type :char *} str_num
 * @return:pre
 */
int count = 0;
node_stu* findnum(node_stu* head, char* str_num) {
    node_stu *p = head, *pre;
    pre = p;
    p = p->next;
    count = 0;
    while (p && override_strcmp(p->num_stu, str_num)) {
        pre = p;
        p = p->next;
    }
    // bool start = true;
    if (!p) {
        printf("Not found\n");
        return NULL;
    } else {
        while (p && !override_strcmp(p->num_stu, str_num)) {
            printf("num:%s %s %s %s %s %s %s %s\n", p->num_stu, p->name, p->age,
                   p->sex, p->birth, p->addr, p->email, p->tele);
            /*  if (start) {
                 pre = p;
                 start = false;
             } */
            p = p->next;
            count++;
        }
    }
    return pre;
}

/**
 * @description: 按姓名查询(学号相同时激发学号查询)
 * 延迟绑定,这里findname()的返回值是找到的上一个结点,但是输出不延迟
 * @param {type :node_stu*} head
 * @param {type :char *} str_num
 * @return:pre
 */
node_stu* findname(node_stu* head, char* str_name) {
    // 延迟绑定效应
    node_stu *p = head,
             *pre;  // 返回值pre表示找到的第一个结点的上一个结点
    pre = p;
    p = p->next;
    count = 0;
    while (p && override_strcmp(p->name, str_name)) {
        pre = p;
        p = p->next;
    }
    // bool start = true;
    if (!p) {
        printf("Not found\n");
    } else {
        while (p && !override_strcmp(p->name, str_name)) {
            printf("name:%s %s %s %s %s %s %s %s\n", p->num_stu, p->name,
                   p->age, p->sex, p->birth, p->addr, p->email, p->tele);
            /* if (start) {
                pre = p;
                start = false;
            } */
            p = p->next;
            count++;
        }
    }
    return pre;
}

/**
 * @description: 修改结点时modify的第三个变量应为真,删除结点时为假
 * 学号不允许重复
 * @param {type:node_stu* } head
 * @param {type: char *} num
 * @param {type: bool} modify
 * @return: head
 */
node_stu* modify_num(node_stu* head, char* num, bool modify) {
    node_stu *p, *pre, *find;  // pre 为p的上一个结点
    find = findnum(head,
                   num);  // 在findnum中延迟绑定,找到符合条件的结点的上一个结点
    int loop = count;
    pre = find;
    p = find->next;
    printf("The total of num of %s is:%d\n", num, count);
    if (loop > 1) {
        char Tofindname[STR_SIZE];
        printf("输入名字\n");
        scanf("%s", Tofindname);
        p = find->next;
        while (loop--) {
            if (!override_strcmp(p->name, Tofindname))
                break;
            pre = p;
            p = p->next;
        }
    }
    /* if (loop < 1) {
        printf("Not found\n");
    } */

    if (modify) {
        printf("update data:\n");
        /* scanf("%s %s %s %s %s %s %s %s", p->num_stu, p->name, p->age, p->sex,
              p->birth, p->addr, p->email, p->tele); */
        head = insert(head);
    } else {
        pre->next = p->next;
        free(p);
    }
    return head;
}

/**
 * @description: 修改结点时modify的第三个变量应为真,删除结点时为假
 * 重名但学号不同
 * 要解决一个先查找num后, 后在重复num中删除name
 * @param {type:node_stu* } head
 * @param {type: char *} name
 * @param {type: bool} modify
 * @return: head
 */
node_stu* modify_name(node_stu* head, char* name, bool modify) {
    node_stu *p, *pre, *find;
    find = findname(head, name);
    int loop = count;
    pre = find;
    p = find->next;
    printf("The total of name of %s is:%d\n", name, count);
    if (loop > 1) {
        char Tofindnum[STR_SIZE];
        printf("输入学号\n");
        scanf("%s", Tofindnum);
        p = find->next;
        while (loop--) {
            if (!override_strcmp(p->name, Tofindnum))
                break;
            pre = p;
            p = p->next;
        }
    }
    /* if(loop < 1){
        printf("Not found\n");
    } */
    if (modify) {
        printf("update data:\n");
        /* scanf("%s %s %s %s %s %s %s %s", p->num_stu, p->name, p->age, p->sex,
              p->birth, p->addr, p->email, p->tele); */
        head = insert(head);
    } else {
        pre->next = p->next;
        free(p);
    }
    return head;
}

/**
 * @description: 重写strcmp函数,使可以优先判断模拟数组的长度,防止数字2排在10之后
 * @param {type :char*} str1
 * @param {type :char*} str2
 * @return: re
 */
int override_strcmp(char* str1, char* str2) {
    int re;
    int lenstr1 = 0;
    int lenstr2 = 0;
    int i;
    for (i = 0; str1[i] != 0; i++) {
        lenstr1++;
    }
    for (i = 0; str2[i] != 0; i++) {
        lenstr2++;
    }
    if (lenstr1 < lenstr2) {
        re = -1;
    } else if (lenstr1 > lenstr2) {
        re = 1;
    } else {
        re = strcmp(str1, str2);
    }

    return re;
}

/**
 * @description: 释放除头结点外的所有节点的内存
 * @param {type}
 * @return: head;
 */
node_stu* delets(node_stu* head) {
    node_stu *p = head->next, *pre;
    while (p) {
        pre = p;
        p = p->next;
        free(pre);
    }
    return head;
}
《 C程序设计》课程综合性实验报告 开课实验室: 基础三 2013年 6 月24 日 实验题目学生成绩管理系统的设计》 一、实验目的 通过该实验把C语言中基本知识(如:顺序结构、选择结构、循环结构、数组、函数、指针、链表等)得到综合应用。完成学生成绩管理系统基本功能设计,提高学生的编程能力和分析问题、解决问题的能力。 二、设备与环境 硬件:多媒体计算机 软件:Windows系列操作系统、C++语言 三、实验内容 对若干个学生基本信息,包括学号、姓名、至少三门课成绩(:英语、高数、计算机)。 ① 学生基本信息录入; ② 学生基本信息的输出; ③ 求每个人平均成绩; ④ 输出平均分大于80分的同学信息; ⑤ 输出有不及格课程的同学信息; ⑥ 输出平均分最高的同学信息; ⑦ 按学号查询学生信息; ⑧ 按学号修改某学生信息并输出; ⑨ 添加某学生信息; ⑩ 删除某学生信息; ⑪ 根据平均成绩综合排名;(此功能用链表处理的同学可不做) 2、实验要求 a、用结构体数组或链表存储学生信息; b、基本功能用函数实现; c、至少完成实验内容中6个功能,学生可以自己增加或减少题目功能; d、程序能正确执行、输入与输出有必要的提示信息、界面美观; e、 用菜单调用加分。使用链表加分。 四、实验结果及分析 1.运行结果图 2. 分析程序 数据类型定义:结构体类型 函数: input(struct student stu[]); 学生基本信息录入 average(struct student stu[]); 求每个人的平均分 print(struct student stu[]); 学生基本信息的输出 chazhao(struct student stu[]); 按学号查询学生信息 paixu(struct student stu[]); 根据平均成绩综合排名 pingjun80(struct student stu[]); 输出平均分大于80分的同学信息 xiugai(struct student stu[]); 按学号修改某学生信息并输出
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值