从0开始写c语言课设
项目简述
管理系统是C语言课设的经典项目,但是在chatGPT已经兴起的今天依然有不少同学因课设而困扰,本文将从0开始写一个简单的药店管理系统。
项目开源于 GitHub
代码语言:javascript复制flowchart TD;
login-->read_from_file
login-->encrypt-->login
read_from_file-->*head
*head-->add_drug
*head-->modify_drug
*head-->delete_drug
*head-->insert_drug
*head-->drug_sort
*head-->search
add_drug-->save_to_file
modify_drug-->save_to_file
delete_drug-->save_to_file
drug_sort-->save_to_file
insert_drug-->save_to_file
search-->save_to_file
代码语言:javascript复制flowchart TD;
main-->login
login-->encrypt
encrypt-->login
login-->read_from_file
read_from_file-->add_drug
read_from_file-->modify_drug
read_from_file-->delete_drug
read_from_file-->insert_drug
read_from_file-->drug_sort
read_from_file-->search
search -->id
search -->name
search -->stock
add_drug-->save_to_file
modify_drug-->save_to_file
delete_drug-->save_to_file
drug_sort-->save_to_file
insert_drug-->save_to_file
search-->save_to_file
开发环境
- 操作系统:Windows 10
- CMake:3.10
- GCC:mingw32 6.3
- IDE:Clion
项目结构
12345678910111213141516171819202122232425262728293031323334353637383940 | .├── main.c├── CMakeLists.txt├── README.md├── .gitignore├── include│ ├── main.h│ └── actions│ ├── add.h│ ├── delete.h│ ├── sort.h│ ├── modify.h│ ├── search.h│ ├── insert.h│ ├── search.h│ └── date│ ├── date.h│ └── log│ ├── log.h│ └── user│ ├── user.h ├── src│ ├── actions│ ├── add.c│ ├── delete.c│ ├── sort.c│ ├── modify.c│ ├── search.c│ ├── insert.c│ ├── search.c│ └── date│ ├── date.c│ └── log│ ├── log.c│ └── user│ ├── user.c ├── build├── config│ ├── drugs.txt├── cmake-build-debug |
---|
项目设计
功能简介
代码语言:javascript复制flowchart TB
药店管理系统-->账号注册/登录
账号注册/登录-->创建药品
账号注册/登录-->修改药品
账号注册/登录-->展示现有药品
账号注册/登录-->删除药品
账号注册/登录-->搜索药品
搜索药品-->简单搜索
搜索药品-->复合搜索
账号注册/登录-->排序
排序-->按价格排序
排序-->按库存排序
排序-->按名字排序
排序-->按类别排序
账号注册/登录-->统计
药品属性
代码语言:javascript复制flowchart TB
药品属性-->药品名
药品属性-->药品类别
药品属性-->药品价格
药品属性-->药品库存
药品属性-->药品生产日期
药品属性-->药品有效期
药品属性-->药品生产厂家
时间计划
代码语言:javascript复制gantt
信息录入,信息查看 :des1, 2023-02-27,2023-02-28
信息删除,信息保存 :des2, after des1, 1d
搜索药品 :des3, after des2, 2d
信息统计 :des4, after des3, 1d
实现功能
我的博客mermaid支持存在一些问题,所以链表操作的示意图后面会补上 ### main.h 通过一个结构体定义了药品信息,包括药品id、药品名称、药品类型、药品价格、药品库存、生产日期、保质期、生产厂家等信息。生产日期格式为yyyy-mm-dd
1234567891011121314151617 | #ifndef C_CURRICULUM_DESIGN_MAIN_H#define C_CURRICULUM_DESIGN_MAIN_Hstruct drug { int id; char name[50]; char type[50]; float price; int stock; char production_date[50]; // 生产日期 char expiration_date[50]; // 保质期 char manufacturer[50]; // 生产厂家};struct node { struct drug data; struct node *next;};#endif //C_CURRICULUM_DESIGN_MAIN_H |
---|
登录
登陆部分复用了大二同学的一个项目,方法是写死一个管理员账号和密码,然后输入账号密码进行验证,验证成功后进入主菜单,否则重新输入,有三次输入机会。
user.h
1234 | #ifndef C_CURRICULUM_DESIGN_USER_H#define C_CURRICULUM_DESIGN_USER_Hint login();#endif //C_CURRICULUM_DESIGN_USER_H |
---|
user.c
123456789101112131415161718192021222324252627282930313233343536373839 | int login(){ int n, i; char pass[15] = "abc123", ch[15] = "12345678"; printf("please enter the correct username and password: (you have three chances to enter) n//username: 12345678; correct password: abc123"); for (i = 0; i < 3; i ) { char pass1[15], ch1[15]; printf("**************************************************n"); printf("--------------------------------------------------n"); printf("tttloginn"); printf("--------------------------------------------------n"); printf("account:n"); gets(ch1); printf("--------------------------------------------------n"); printf("password:n"); gets(pass1); printf("--------------------------------------------------n"); printf("**************************************************nnn"); if (strcmp(ch, ch1) == 0 && strcmp(pass, pass1) == 0) { // 两字符串相等时为0 printf("password correctn"); system("cls"); return 0; } else { printf("wrong,please try again"); printf("you only have %d chance(s) to try", 3 - i - 1); if (3 - 1 - i == 0) { return 1; } } system("cls"); }} |
---|
文件读取与保存
这里的功能是直接定义在main.c
文件中的
main.c
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263 | char config_path[100] = "../config/drugs.txt";//文件路径,../是因为main.c和config文件夹在同一目录下,但是编译后的可执行文件在build文件夹下,所以需要返回上一级目录struct node *read_from_file(){ FILE *fp = fopen(config_path, "r "); if (fp == NULL) { printf("Error: Cannot open file.n"); exit(1); } struct node *head = NULL; struct node *tail = NULL; char line[100]; while (fgets(line, 100, fp) != NULL) { printf("len:%d , %s",strlen(line),line); struct drug new_drug; if(strlen(line) == 1) continue; sscanf(line, "%d,%[^,],%[^,],%f,%d,%[^,],%[^,],%[^,]", &new_drug.id, new_drug.name, new_drug.type, &new_drug.price, &new_drug.stock, new_drug.production_date, new_drug.expiration_date, new_drug.manufacturer); struct node *new_node = (struct node *)malloc(sizeof(struct node)); new_node->data = new_drug; new_node->next = NULL; if (head == NULL) { head = new_node; tail = new_node; } else { tail->next = new_node; tail = new_node; } } fclose(fp); return head;}void save_to_file(struct node *head){ FILE *fp = fopen(config_path, "w "); if (fp == NULL) { printf("Error: Cannot open file.n"); exit(1); } struct node *current = head; while (current != NULL) { fprintf(fp, "%d,%s,%s,%.2f,%d,%s,%s,%sn", current->data.id, current->data.name, current->data.type, current->data.price, current->data.stock, current->data.production_date, current->data.expiration_date, current->data.manufacturer); current = current->next; } fclose(fp);} |
---|
添加药品
这里分两种情况讨论 1. 头节点为空,直接添加 2. 头节点不为空,遍历链表,找到最后一个节点,然后添加
add.c
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748 | #include <stdio.h>#include <stdlib.h>#include <string.h>#include "main.h"#include "date/date.h"void add_drug(struct node **head) { struct drug new_drug; printf("Enter drug ID: "); scanf("%d", &new_drug.id); printf("Enter drug name: "); scanf("%s", new_drug.name); printf("Enter drug type: n"); printf("1 means prescription drugs , and 2 means OTC(over-the-counter drugs)"); scanf("%s", new_drug.type); printf("Enter drug price: "); scanf("%f", &new_drug.price); printf("Enter drug stock: "); scanf("%d", &new_drug.stock); printf("Enter drug production date: n"); printf("the format of date is YYYY-MM-DD ,for example ,2022-03-02"); scanf("%s", new_drug.production_date); printf("Enter drug expiration date(days): "); scanf("%s", new_drug.expiration_date); printf("Enter drug manufacturer: "); scanf("%s", new_drug.manufacturer); struct node *new_node = (struct node*)malloc(sizeof(struct node)); new_node->data = new_drug; new_node->next = NULL; if (*head == NULL) { *head = new_node; } else { struct node *current = *head; while (current->next != NULL) { current = current->next; } current->next = new_node; }} |
---|
add.h
1234 | #ifndef C_CURRICULUM_DESIGN_ADD_H#define C_CURRICULUM_DESIGN_ADD_Hvoid add_drug(struct node **head);#endif //C_CURRICULUM_DESIGN_ADD_H |
---|
删除药品
delete.h
1234 | #ifndef C_CURRICULUM_DESIGN_DELETE_H#define C_CURRICULUM_DESIGN_DELETE_Hvoid delete_drug(struct node **head, int id);#endif //C_CURRICULUM_DESIGN_DELETE_H |
---|
delete.c
123456789101112131415161718192021222324252627 | #include <stdio.h>#include <stdlib.h>#include <string.h>#include "main.h"void delete_drug(struct node **head, int id) { struct node *current = *head; struct node *previous = NULL; while (current != NULL) { if (current->data.id == id) { if (previous == NULL) { *head = current->next; } else { previous->next = current->next; } free(current); printf("Drug with ID %d has been deleted.n", id); return; } previous = current; current = current->next; } printf("Error: Drug with ID %d not found.n", id);} |
---|
修改药品
modify.c
123456789101112131415161718192021222324252627282930313233343536373839404142 | #include <stdio.h>#include <stdlib.h>#include <string.h>#include "main.h"void modify_drug(struct node *head) { int id; printf("Enter the ID of the drug you want to modify: "); scanf("%d", &id); struct node *current = head; while (current != NULL) { if (current->data.id == id) { printf("Enter the new name of the drug: "); scanf("%s", current->data.name); printf("Enter the new type of the drug: n"); printf("1 means prescription drugs , and 2 means OTC(over-the-counter drugs)"); scanf("%s", current->data.type); printf("Enter the new price of the drug: "); scanf("%f", ¤t->data.price); printf("Enter the new stock of the drug: "); scanf("%d", ¤t->data.stock); printf("Enter the new production date of the drug: n"); printf("the format of date is YYYY-MM-DD ,for example ,2022-03-02"); scanf("%s", current->data.production_date); printf("Enter the new expiration date of the drug(days): "); scanf("%s", current->data.expiration_date); printf("Enter the new manufacturer of the drug: "); scanf("%s", current->data.manufacturer); printf("Drug with ID %d has been modified.n", id); return; } current = current->next; } printf("Error: Drug with ID %d not found.n", id);} |
---|
modify.h
1234 | #ifndef C_CURRICULUM_DESIGN_MODIFY_H#define C_CURRICULUM_DESIGN_MODIFY_Hvoid modify_drug(struct node *head);#endif //C_CURRICULUM_DESIGN_MODIFY_H |
---|
查询药品
查询部分采用了及其暴力的方法,给每个要查询的属性写一个函数,然后遍历链表,将所有符合条件的药品都打印出来。
- search.h
1234567 | #ifndef C_CURRICULUM_DESIGN_SEARCH_H#define C_CURRICULUM_DESIGN_SEARCH_H#include "../main.h"void search_by_id(struct node *head, int id);void search_by_name(struct node *head, char *name);void search(struct node *head);#endif //C_CURRICULUM_DESIGN_SEARCH_H |
---|
search.c
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869 | #include <stdio.h>#include <stdlib.h>#include <string.h>#include "main.h"#include "log/log.h"void search_by_id(struct node *head, int id) { struct node *current = head; int found = 0; while (current != NULL) { if (current->data.id == id) { print_drug(current->data); found = 1; } current = current->next; } if (!found) { printf("No drug with ID %d found.n", id); }}void search_by_name(struct node *head, char *name) { struct node *current = head; int found = 0; while (current != NULL) { if (strcmp(current->data.name, name) == 0) { print_drug(current->data); found = 1; } current = current->next; } if (!found) { printf("No drug with name "%s" found.n", name); }}void search(struct node *head) { int choice, id; char name[50]; printf("Choose a search criteria:n"); printf("1. IDn"); printf("2. Namen"); printf("Enter your choice: "); scanf("%d", &choice); switch (choice) { case 1: printf("Enter the ID of the drug to search: "); scanf("%d", &id); search_by_id(head, id); break; case 2: printf("Enter the name of the drug to search: "); scanf("%s", name); search_by_name(head, name); break; default: printf("Invalid choice. Please try again.n"); break; }} |
---|
插入药品
这是一个我也不知道为什么要写的功能,但是我还是写了,就是在链表中插入一个药品,插入的位置是按照ID来搜索的。 - insert.h
1234 | #ifndef C_CURRICULUM_DESIGN_INSERT_H#define C_CURRICULUM_DESIGN_INSERT_Hvoid insert_drug(struct node **head, int last_id);#endif //C_CURRICULUM_DESIGN_INSERT_H |
---|
insert.c
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455 | #include <stdio.h>#include <stdlib.h>#include <string.h>#include "main.h"void insert_drug(struct node **head , int last_id){ struct drug new_drug; printf("Enter drug ID: "); scanf("%d", &new_drug.id); printf("Enter drug name: "); scanf("%s", new_drug.name); printf("Enter drug type: n"); printf("1 means prescription drugs , and 2 means OTC(over-the-counter drugs)"); scanf("%s", new_drug.type); printf("Enter drug price: "); scanf("%f", &new_drug.price); printf("Enter drug stock: "); scanf("%d", &new_drug.stock); printf("Enter drug production date: n"); printf("the format of date is YYYY-MM-DD ,for example ,2022-03-02"); scanf("%s", new_drug.production_date); printf("Enter drug expiration date: "); scanf("%s", new_drug.expiration_date); printf("Enter drug manufacturer: "); scanf("%s", new_drug.manufacturer); struct node *new_node = (struct node *)malloc(sizeof(struct node)); new_node->data = new_drug; new_node->next = NULL; if (*head == NULL) { *head = new_node; } else { //将新节点插入到last_id所在的位置之后 struct node *current = *head; while (current->data.id != last_id) { current = current->next; } new_node->next = current->next; current->next = new_node; }} |
---|
### 统计药品 统计药品的功能是统计出当前库存中的药品的总数,总价值。
count.h
1234567 | #ifndef C_CURRICULUM_DESIGN_COUNT_H#define C_CURRICULUM_DESIGN_COUNT_H#include "../main.h"int countMedicine(struct node * head);float countTotalValue(struct node* head);#endif //C_CURRICULUM_DESIGN_COUNT_H |
---|
count.c
1234567891011121314151617181920212223 | #include <stdio.h>#include <stdlib.h>#include <string.h>#include "main.h"int countMedicine(struct node* head) { int count = 0; struct node* p = head; while (p != NULL) { count ; p = p->next; } return count;}float countTotalValue(struct node* head) { float totalValue = 0.0; struct node* p = head; while (p != NULL) { totalValue = (p->data.price * p->data.stock); p = p->next; } return totalValue;} |
---|
单个节点输出
这个函数是为了方便输出单个节点的数据,因为在输出链表的时候,每个节点都要输出,所以我就把这个函数单独拿出来了。
请注意,这个函数式在log文件夹下面的 - log.h
12345678 | #ifndef C_CURRICULUM_DESIGN_LOG_H#define C_CURRICULUM_DESIGN_LOG_H#include <stdio.h>#include <stdlib.h>#include <string.h>#include "../main.h"void print_drug(struct drug d);#endif //C_CURRICULUM_DESIGN_LOG_H |
---|
log.c
12345678 | #include <stdio.h>#include <stdlib.h>#include <string.h>#include "main.h"void print_drug(struct drug d) {// printf("%-10d%-20s%-10dn", d.id, d.name, d.stock); printf("ID: %d, Name: %s, Type: %s, Price: %.2f, Stock: %d, Production Date: %s, Expiration Date: %s, Manufacturer: %sn", d.id, d.name, d.type, d.price, d.stock, d.production_date, d.expiration_date, d.manufacturer);} |
---|
日期处理
这个函数是为了处理日期,因为我在输入药品的时候,日期是以字符串的形式输入的,所以我就写了这个函数,把字符串转换成日期。
这个功能因为跨平台的问题,可能不能很好的运行。此外,在Linux下可以使用strptime()
这个函数来做日期的转换,但是在Windows下没有这个函数。
date.h
1234 | #ifndef C_CURRICULUM_DESIGN_DATE_H#define C_CURRICULUM_DESIGN_DATE_Hchar* getOverDate(char* productionDate, char shelfLife_str);#endif //C_CURRICULUM_DESIGN_DATE_H |
---|
date.c
12345678910111213141516171819202122232425262728293031323334353637383940 | #include <stdio.h>#include <stdlib.h>#include <string.h>//#include <time.h>// On Windows platform, the function strptime() from time.h is not supported, I found.char* getOverDate(char* productionDate, char* shelfLife_str) { char* temp= shelfLife_str; int shelfLife = atoi(temp); // 将生产日期字符串按照 "-" 分割为年、月、日三个字符串 char* year = strtok(productionDate, "-"); char* month = strtok(NULL, "-"); char* day = strtok(NULL, "-"); // 将年、月、日字符串转换为整型变量 int year_int = atoi(year); int month_int = atoi(month); int day_int = atoi(day); // 计算过期日期的年、月、日 year_int = (shelfLife / 365); shelfLife %= 365; month_int = (shelfLife / 30); day_int = (shelfLife % 30); if (day_int > 30) { day_int -= 30; month_int ; } if (month_int > 12) { month_int -= 12; year_int ; } // 将过期日期的年、月、日转换为字符串 char* expirationDate = (char*) malloc(sizeof(char) * 11); sprintf(expirationDate, "d-d-d", year_int, month_int, day_int); return expirationDate;} |
---|
功能整合
将上面的功能整合起来,就是我们的主函数了。
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 | #include <stdio.h>#include <stdlib.h>#include <string.h>#include "main.h"#include "actions/count.h"#include "actions/search.h"#include "log/log.h"#include "user/user.h"#include "actions/add.h"#include "actions/delete.h"#include "actions/modify.h"#include "actions/sort.h"#include "actions/insert.h"char config_path[100] = "../config/drugs.txt";struct node *read_from_file(){ FILE *fp = fopen(config_path, "r "); if (fp == NULL) { printf("Error: Cannot open file.n"); exit(1); } struct node *head = NULL; struct node *tail = NULL; char line[100]; while (fgets(line, 100, fp) != NULL) { struct drug new_drug; if(strlen(line) == 1) continue; sscanf(line, "%d,%[^,],%[^,],%f,%d,%[^,],%[^,],%[^,]", &new_drug.id, new_drug.name, new_drug.type, &new_drug.price, &new_drug.stock, new_drug.production_date, new_drug.expiration_date, new_drug.manufacturer); struct node *new_node = (struct node *)malloc(sizeof(struct node)); new_node->data = new_drug; new_node->next = NULL; if (head == NULL) { head = new_node; tail = new_node; } else { tail->next = new_node; tail = new_node; } } fclose(fp); return head;}void save_to_file(struct node *head){ FILE *fp = fopen(config_path, "w "); if (fp == NULL) { printf("Error: Cannot open file.n"); exit(1); } struct node *current = head; while (current != NULL) { fprintf(fp, "%d,%s,%s,%.2f,%d,%s,%s,%sn", current->data.id, current->data.name, current->data.type, current->data.price, current->data.stock, current->data.production_date, current->data.expiration_date, current->data.manufacturer); current = current->next; } fclose(fp);}int main(){ int t; t = login(); if (t == 1) { printf("you have entered the wrong password for three times, the program will exit."); return 0; } struct node *head = read_from_file(); int option = -1; while (option != 0) { printf("Please select an option:n"); printf("1. Add a new drugn"); printf("2. Modify an existing drugn"); printf("3. Display drug listn"); printf("4. Delete a drugn"); printf("5. Searchn"); printf("6. Sortn"); printf("7. Statisticsn"); printf("8. insertn"); printf("0. Quitn"); scanf("%d", &option); if (option == 1) { add_drug(&head); int save_option = 0; printf("Save changes to file?n"); printf("1. Yesn"); printf("2. Non"); scanf("%d", &save_option); if (save_option == 1) { save_to_file(head); } } else if (option == 2) { modify_drug(head); int save_option = 0; printf("Save changes to file?n"); printf("1. Yesn"); printf("2. Non"); scanf("%d", &save_option); if (save_option == 1) { save_to_file(head); } } else if (option == 3) { struct node *current = head; while (current != NULL) { print_drug(current->data); printf("n"); current = current->next; } } else if (option == 4) { int delete_id; scanf("%d", &delete_id); printf("%dn", delete_id); delete_drug(&head, delete_id); } else if (option == 5) { int search_option = 0; printf("Choose a search criteria:n"); printf("1. Simple searchn"); printf("2. Complex searchn"); scanf("%d", &search_option); if (search_option == 1) { search(head); } else if (search_option == 2) { search_complex(head); } else { printf("Invalid option. Please try again.n"); } } else if (option == 6) { drug_sort(head); } else if (option == 7) { printf("total:%dn", countMedicine(head)); printf("total price:%.2fn", countTotalValue(head)); } else if (option == 8) { int insert_id; scanf("%d", &insert_id); insert_drug(&head, insert_id); } else if (option == 0) { printf("Bye!n"); } else { printf("Invalid option. Please try again.n"); } } save_to_file(head); return 0;} |
---|
项目编译
Windows
Clion/VScode/others
使用自带的cmake工具或插件进行编译
bash
1234 | mkdir buildcd ./buildcmake .. -G "MinGW Makefiles"cmake --build . |
---|
Linux/MacOS
1234 | mkdir buildcd ./buildcmake ..make |
---|
github actions
在.github/workflows
文件夹下创建文件build.yml
12345678910111213141516171819202122232425 | name: Build on Ubuntuon: push: branches: - master pull_request: branches: - masterjobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 with: ref: master - name: Get CMake uses: symbitic/install-cmake@master - name: build run: | mkdir build cd ./build cmake .. make |
---|
然后上传到Github即可自动编译。如果你希望获取编译好的文件,可以在最后加上将编译好的文件push到master分支的操作。