一、来源
本项目为学号 2452404 同学的 C 语言课程大作业 —— 医院信息管理系统初始版本,该版本实现了病人信息管理、药品管理、数据持久化等基础功能,本次作业针对该系统存在的缺陷进行二次开发与优化。
二、运行环境 + 运行结果截图
运行环境:
操作系统:Windows
编译环境:Visual Studio 2022
依赖:标准 C 库(stdio.h、stdlib.h、string.h 等),无额外第三方依赖
初始版本运行结果截图:
(1)管理员登录界面:输入默认账号 admin、密码 admin123 可成功登录系统,登录失败 3 次则退出程序;

(2)主菜单界面:显示 9 个功能选项(注册新病人、查询病人信息、显示所有病人、病人消费、保存数据、查询药品库存、病人管理菜单、药品管理菜单、退出系统);






(3)药品管理界面:首次运行可加载默认初始化的 4 种药品(阿莫西林胶囊、布洛芬片、感冒灵颗粒、维生素 C 片);

(4)数据保存功能:可将病人信息保存至 patients.txt、药品信息保存至 drugs.txt,重启系统可加载文件数据

5.代码介绍
(1)核心结构体
Patient(病人结构体):核心基础结构体,表示病人信息,包含 ID、姓名、年龄、联系电话、病历信息、总消费金额、删除状态、链表指针等属性,是病人管理模块的核心数据载体。
Drug(药品结构体):核心基础结构体,表示药品信息,包含药品名称、库存数量、单价、删除状态等属性,是药品管理模块的核心数据载体。
(2)功能模块
管理员认证模块:包含admin_login()函数,负责管理员账号密码验证,限制登录尝试次数(3 次),验证失败则退出系统,是系统安全访问的入口。
病人管理模块:核心功能包括病人添加(add_new_patient())、信息查询(search_patient())、信息修改(modify_patient_info())、删除 / 恢复(delete_patient()/restore_patient())、列表展示(list_all_patients()/list_deleted_patients()),覆盖病人全生命周期管理。
药品管理模块:核心功能包括药品添加(add_new_drug())、信息修改(modify_drug())、删除(delete_drug())、列表展示(list_all_drugs())、库存查询(show_drug_inventory())、清单导出(export_drug_list()),实现药品基础管理。
消费管理模块:核心功能为patient_consumption(),负责关联病人与药品,实现病人购买药品的消费记录、库存扣减、消费金额累计。
数据持久化模块:包含病人数据保存 / 加载(save_patient_data()/load_patient_data())、药品数据保存 / 加载(save_drug_data()/load_drug_data()),实现数据的文件存储与读取。
(3)数据存储
病人信息存储在patients.txt文件,包含 ID、姓名、年龄、电话、状态、病历、总消费等字段,采用固定宽度文本格式存储。
药品信息存储在drugs.txt文件,包含药品名称、库存数量、单价、状态等字段,采用固定宽度文本格式存储。
无独立的配置文件,管理员账号密码(admin/admin123)通过宏定义硬编码在代码中。
初始源代码
点击查看代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX_ID_LEN 20//病人ID
#define MAX_NAME_LEN 20//病人姓名
#define MAX_RECORD_LEN 31// 病人信息改为30字+1个结尾符
#define MAX_DRUG_NAME_LEN 30//药品名称
#define MAX_DRUGS 100//新增:药品数量
#define MAX_PHONE_LEN 12// 新增:病人电话:11位数字+1个结尾符
#define ADMIN_USERNAME "admin"//新增:管理员账号
#define ADMIN_PASSWORD "admin123"//新增:管理员密码
// 病人信息结构
typedef struct Patient {
char id[MAX_ID_LEN];//ID
char name[MAX_NAME_LEN];//姓名
char record[MAX_RECORD_LEN];// 病历(30字以内)
char phone[MAX_PHONE_LEN];//新增:联系电话(11位数字)
int age;//新增:年龄
int is_deleted;//新增:状态(1-被删除,0-正常)
float total_consume;//总消费
struct Patient* next;//链表存储-下一个病人地址
} Patient;
// 药品信息结构
typedef struct {
char name[MAX_DRUG_NAME_LEN];//药品名称
int stock;//数量
float price;//价格
int is_deleted;//状态(1-被删除,0-正常)
} Drug;
// 全局变量
Patient* patient_head = NULL;//病人头指针置空
Drug drugs[MAX_DRUGS];//新增:药品数组(容量100)
int drug_count = 0;//新增:药品数量
// 函数声明
void admin_login();//新增:管理员登录
void modify_patient_info();//新增:修改病人信息
void delete_patient();//新增:删除病人
void restore_patient();//新增:恢复被删除的病人
void list_deleted_patients();//新增:列出被删除的病人
Patient* find_patient_by_id(const char* id, int include_deleted);//病人管理函数
void initialize_drugs();//初始化药品数据(默认数据)
void show_main_menu();//显示主菜单
void show_patient_menu();//新增:显示病人管理子菜单
void show_drug_menu();//新增:显示药品管理子菜单
void add_new_patient();//增加新病人
void search_patient();//查询病人
void list_all_patients();//显示所有病人
void patient_consumption();//病人消费
void save_patient_data();//新增:保存病人数据到文件(txt文件)
void save_drug_data();//新增:保存药品数据到文件(txt文件)
void load_patient_data();//新增:从patients.txt文件加载病人数据(txt文本格式)
void load_drug_data();//新增:从drugs.txt文件加载病人数据(txt文本格式)
void show_drug_inventory();//显示药品库存(库存预警)
void clear_input_buffer();//清空输入缓冲区
void free_all_memory();//释放所有内存(病人链表)
void drug_management();//新增:药品管理函数
void add_new_drug();
//新增:智能添加药品:
//药品已存在且已删除->恢复并增加库存
//药品已存在且未删除->增加库存
//药品不存在->添加新药品
void modify_drug();//新增:修改药品信息
void delete_drug();//新增:删除药品
void export_drug_list();//新增:导出药品清单到文件
Drug* find_drug_by_name(const char* name, int include_deleted);//查询药品
void list_all_drugs();//新增:显示所有药品
void format_print(const char* label, const char* value, int label_width);// 格式化输出字符串
void format_print_int(const char* label, int value, int label_width);// 格式化输出整数
void format_print_float(const char* label, float value, int label_width);// 格式化输出浮点数
int validate_phone(const char* phone); // 新增:验证电话号码是否为11位数字
// 主函数
int main() {
// 初始化系统
load_drug_data();
load_patient_data();
// 管理员登录
admin_login();
int choice;
do {
show_main_menu();
printf("请选择: ");
if (scanf("%d", &choice) != 1) {
printf("输入无效,请输入数字!\n");
clear_input_buffer();
continue;
}
clear_input_buffer();
switch (choice) {
case 1:
add_new_patient();
break;
case 2:
search_patient();
break;
case 3:
list_all_patients();
break;
case 4:
patient_consumption();
break;
case 5:
save_patient_data();
save_drug_data();
printf("数据已保存到文件!\n");
break;
case 6:
show_drug_inventory();
break;
case 7:
show_patient_menu();
break;
case 8:
drug_management();
break;
case 9:
save_patient_data();
save_drug_data();
printf("感谢使用医院管理系统,再见!\n");
break;
default:
printf("无效的选择,请重新输入!\n");
}
if (choice != 9) {
printf("\n按回车键继续...");
getchar();
}
} while (choice != 9);
// 释放内存
free_all_memory();
return 0;
}
// 验证电话号码是否为11位数字
int validate_phone(const char* phone) {
if (strlen(phone) != 11) {
return 0;
}
for (int i = 0; i < 11; i++) {
if (!isdigit(phone[i])) {
return 0;
}
}
return 1;
}
// 格式化输出字符串
void format_print(const char* label, const char* value, int label_width) {
printf("%-*s: %s\n", label_width, label, value);
}
// 格式化输出整数
void format_print_int(const char* label, int value, int label_width) {
printf("%-*s: %d\n", label_width, label, value);
}
// 格式化输出浮点数
void format_print_float(const char* label, float value, int label_width) {
printf("%-*s: %.2f\n", label_width, label, value);
}
// 管理员登录验证
void admin_login() {
char username[MAX_NAME_LEN];
char password[20];
int attempts = 3;
while (attempts > 0) {
system("cls");
printf("===============================\n");
printf(" 医院信息管理系统\n");
printf("===============================\n");
printf(" 管理员登录\n");
printf("-------------------------------\n");
printf("请输入管理员用户名: ");
scanf("%19s", username);
clear_input_buffer();
printf("请输入管理员密码: ");
scanf("%19s", password);
clear_input_buffer();
// 验证管理员账号密码
if (strcmp(username, ADMIN_USERNAME) == 0 &&
strcmp(password, ADMIN_PASSWORD) == 0) {
printf("\n管理员登录成功!欢迎进入系统\n");
printf("按回车键继续...");
getchar();
return;
}
attempts--;
if (attempts > 0) {
printf("\n登录失败!用户名或密码错误,还剩 %d 次尝试机会\n", attempts);
printf("按回车键继续...");
getchar();
}
}
printf("\n登录失败次数过多,系统将退出!\n");
exit(0);
}
// 显示主菜单
void show_main_menu() {
system("cls");
printf("===============================\n");
printf(" 医院信息管理系统\n");
printf("===============================\n");
printf(" 1. 注册新病人\n");
printf(" 2. 查询病人信息\n");
printf(" 3. 显示所有病人\n");
printf(" 4. 病人消费\n");
printf(" 5. 保存数据\n");
printf(" 6. 查询药品库存\n");
printf(" 7. 病人管理菜单\n");
printf(" 8. 药品管理菜单\n");
printf(" 9. 退出系统\n");
printf("===============================\n");
}
// 显示病人管理菜单
void show_patient_menu() {
int choice;
do {
system("cls");
printf("========================\n");
printf(" 病人管理菜单\n");
printf("========================\n");
printf(" 1. 修改病人信息\n");
printf(" 2. 删除病人\n");
printf(" 3. 恢复已删除病人\n");
printf(" 4. 查看已删除病人\n");
printf(" 5. 返回主菜单\n");
printf("========================\n");
printf("请选择: ");
if (scanf("%d", &choice) != 1) {
printf("输入无效,请输入数字!\n");
clear_input_buffer();
continue;
}
clear_input_buffer();
switch (choice) {
case 1:
modify_patient_info();
break;
case 2:
delete_patient();
break;
case 3:
restore_patient();
break;
case 4:
list_deleted_patients();
break;
case 5:
return;
default:
printf("无效的选择,请重新输入!\n");
}
if (choice != 5) {
printf("\n按回车键继续...");
getchar();
}
} while (choice != 5);
}
// 显示药品管理菜单
void show_drug_menu() {
system("cls");
printf("===============================\n");
printf(" 药品管理菜单\n");
printf("===============================\n");
printf(" 1. 显示所有药品\n");
printf(" 2. 增加药品\n");
printf(" 3. 修改药品信息\n");
printf(" 4. 删除药品\n");
printf(" 5. 导出药品清单\n");
printf(" 6. 返回主菜单\n");
printf("===============================\n");
}
// 添加新病人
void add_new_patient() {
Patient* new_patient = (Patient*)malloc(sizeof(Patient));
if (new_patient == NULL) {
printf("内存分配失败!\n");
return;
}
// 初始化病人信息
memset(new_patient, 0, sizeof(Patient));
printf("\n=== 注册新病人 ===\n");
// 输入病人ID
printf("请输入病人ID: ");
scanf("%19s", new_patient->id);
clear_input_buffer();
// 检查ID是否已存在
Patient* current = find_patient_by_id(new_patient->id, 1);
if (current != NULL && current->is_deleted == 0) {
printf("错误:该病人ID已存在!\n");
free(new_patient);
return;
}
// 输入病人姓名
printf("请输入病人姓名: ");
scanf("%19s", new_patient->name);
clear_input_buffer();
// 输入年龄
printf("请输入年龄: ");
scanf("%d", &new_patient->age);
clear_input_buffer();
// 输入联系电话(必须11位数字)
while (1) {
printf("请输入联系电话(11位数字): ");
scanf("%12s", new_patient->phone);
clear_input_buffer();
if (validate_phone(new_patient->phone)) {
break;
}
else {
printf("错误:电话号码必须是11位数字!\n");
}
}
// 输入病历信息(最多30字)
printf("请输入病历信息(最多30字): ");
fgets(new_patient->record, MAX_RECORD_LEN, stdin);
// 去除末尾的换行符
size_t len = strlen(new_patient->record);
if (len > 0 && new_patient->record[len - 1] == '\n') {
new_patient->record[len - 1] = '\0';
}
// 检查病历长度
if (strlen(new_patient->record) > 30) {
printf("警告:病历信息超过30字,已自动截断!\n");
new_patient->record[30] = '\0';
}
new_patient->total_consume = 0.0f;
new_patient->is_deleted = 0;
new_patient->next = patient_head;
patient_head = new_patient;
printf("\n病人注册成功!\n");
format_print("病人ID", new_patient->id, 12);
format_print("病人姓名", new_patient->name, 12);
format_print_int("年龄", new_patient->age, 12);
format_print("联系电话", new_patient->phone, 12);
}
// 修改病人信息
void modify_patient_info() {
printf("\n=== 修改病人信息 ===\n");
if (patient_head == NULL) {
printf("当前没有病人信息!\n");
return;
}
char patient_id[MAX_ID_LEN];
printf("请输入要修改的病人ID: ");
scanf("%19s", patient_id);
clear_input_buffer();
Patient* current = find_patient_by_id(patient_id, 0);
if (current == NULL) {
printf("未找到该病人!\n");
return;
}
printf("\n=== 修改 %s 的信息 ===\n", current->name);
int choice;
size_t len;
do {
printf("\n1. 修改姓名\n");
printf("2. 修改年龄\n");
printf("3. 修改电话\n");
printf("4. 修改病历\n");
printf("5. 完成修改\n");
printf("请选择要修改的项目: ");
scanf("%d", &choice);
clear_input_buffer();
switch (choice) {
case 1:
printf("请输入新的姓名(原:%s): ", current->name);
scanf("%19s", current->name);
clear_input_buffer();
printf("姓名修改成功!\n");
break;
case 2:
printf("请输入新的年龄(原:%d): ", current->age);
scanf("%d", ¤t->age);
clear_input_buffer();
printf("年龄修改成功!\n");
break;
case 3:
while (1) {
printf("请输入新的电话(原:%s,必须11位数字): ", current->phone);
scanf("%11s", current->phone);
clear_input_buffer();
if (validate_phone(current->phone)) {
printf("电话修改成功!\n");
break;
}
else {
printf("错误:电话号码必须是11位数字!\n");
}
}
break;
case 4:
printf("请输入新的病历(原:%s,最多30字): ", current->record);
fgets(current->record, MAX_RECORD_LEN, stdin);
len = strlen(current->record);
if (len > 0 && current->record[len - 1] == '\n') {
current->record[len - 1] = '\0';
}
// 检查病历长度
if (strlen(current->record) > 30) {
printf("警告:病历信息超过30字,已自动截断!\n");
current->record[30] = '\0';
}
printf("病历修改成功!\n");
break;
case 5:
printf("信息修改完成!\n");
break;
default:
printf("无效的选择!\n");
}
} while (choice != 5);
}
// 删除病人
void delete_patient() {
printf("\n=== 删除病人 ===\n");
if (patient_head == NULL) {
printf("当前没有病人信息!\n");
return;
}
char patient_id[MAX_ID_LEN];
printf("请输入要删除的病人ID: ");
scanf("%19s", patient_id);
clear_input_buffer();
// 查找病人
Patient* current = patient_head;
while (current != NULL) {
if (current->is_deleted == 0 && strcmp(current->id, patient_id) == 0) {
// 管理员直接删除
printf("确认要删除病人 %s(ID: %s)吗?(y/n): ", current->name, current->id);
char confirm;
scanf("%c", &confirm);
clear_input_buffer();
if (confirm == 'y' || confirm == 'Y') {
// 标记为已删除
current->is_deleted = 1;
printf("病人 %s 已删除!\n", current->name);
}
else {
printf("取消删除操作\n");
}
return;
}
current = current->next;
}
printf("未找到该病人!\n");
}
// 恢复已删除病人
void restore_patient() {
printf("\n=== 恢复已删除病人 ===\n");
char patient_id[MAX_ID_LEN];
printf("请输入要恢复的病人ID: ");
scanf("%19s", patient_id);
clear_input_buffer();
// 查找已删除的病人
Patient* current = patient_head;
while (current != NULL) {
if (current->is_deleted == 1 && strcmp(current->id, patient_id) == 0) {
current->is_deleted = 0;
printf("病人 %s 已恢复!\n", current->name);
return;
}
current = current->next;
}
printf("未找到已删除的病人!\n");
}
// 查看已删除病人
void list_deleted_patients() {
printf("\n=== 已删除病人列表 ===\n");
Patient* current = patient_head;
int count = 0;
int has_deleted = 0;
printf("ID 姓名 年龄 电话 状态\n");
printf("--------------------------------------------------------------------\n");
while (current != NULL) {
if (current->is_deleted == 1) {
has_deleted = 1;
printf("%-20s%-20s%-8d%-18s已删除\n",
current->id,
current->name,
current->age,
current->phone);
count++;
}
current = current->next;
}
if (!has_deleted) {
printf("暂无已删除的病人\n");
}
else {
printf("--------------------------------------------------------------------\n");
printf("总共 %d 位已删除的病人\n", count);
}
}
// 查找病人
Patient* find_patient_by_id(const char* id, int include_deleted) {
Patient* current = patient_head;
while (current != NULL) {
if (strcmp(current->id, id) == 0) {
if (include_deleted || current->is_deleted == 0) {
return current;
}
}
current = current->next;
}
return NULL;
}
// 查询病人信息
void search_patient() {
printf("\n=== 查询病人信息 ===\n");
if (patient_head == NULL) {
printf("当前没有病人信息!\n");
return;
}
char search_id[MAX_ID_LEN];
printf("请输入要查询的病人ID: ");
scanf("%19s", search_id);
clear_input_buffer();
Patient* current = find_patient_by_id(search_id, 0);
if (current != NULL) {
printf("\n=== 病人详细信息 ===\n");
format_print("病人ID", current->id, 12);
format_print("病人姓名", current->name, 12);
format_print_int("年龄", current->age, 12);
format_print("联系电话", current->phone, 12);
format_print("病历信息", current->record, 12);
format_print_float("总消费金额", current->total_consume, 12);
format_print("状态", current->is_deleted ? "已删除" : "正常", 12);
return;
}
printf("未找到ID为 %s 的病人!\n", search_id);
}
// 显示所有病人信息
void list_all_patients() {
printf("\n=== 所有病人信息(正常状态) ===\n");
Patient* current = patient_head;
int count = 0;
int has_patients = 0;
// 简单直接的格式
printf("序号 ID 姓名 年龄 电话 总消费\n");
printf("----------------------------------------------------\n");
while (current != NULL) {
if (current->is_deleted == 0) {
has_patients = 1;
// 减少宽度,确保显示
printf("%-4d %-8s %-8s %-6d %-13s %.2f\n",
++count,
current->id,
current->name,
current->age,
current->phone,
current->total_consume);
}
current = current->next;
}
if (!has_patients) {
printf("暂无病人信息!\n");
}
else {
printf("----------------------------------------------------\n");
printf("总共 %d 位病人\n", count);
}
}
// 病人消费功能
void patient_consumption() {
printf("\n=== 病人消费 ===\n");
if (patient_head == NULL) {
printf("请先注册病人!\n");
return;
}
char patient_id[MAX_ID_LEN];
printf("请输入病人ID: ");
scanf("%19s", patient_id);
clear_input_buffer();
// 查找病人(只找未删除的)
Patient* current = find_patient_by_id(patient_id, 0);
if (current == NULL) {
printf("未找到该病人!\n");
return;
}
// 显示药品列表
printf("\n=== 药品列表 ===\n");
printf("编号 药品名称 库存 单价(元)\n");
printf("-----------------------------------------\n");
for (int i = 0; i < drug_count; i++) {
if (drugs[i].is_deleted == 0) {
printf("%-6d%-20s%-8d%.2f\n",
i + 1,
drugs[i].name,
drugs[i].stock,
drugs[i].price);
}
}
// 选择药品
int drug_choice;
printf("\n请选择药品编号(1-%d): ", drug_count);
if (scanf("%d", &drug_choice) != 1) {
printf("输入无效!\n");
clear_input_buffer();
return;
}
clear_input_buffer();
if (drug_choice < 1 || drug_choice > drug_count) {
printf("无效的药品编号!\n");
return;
}
int drug_index = drug_choice - 1;
// 检查药品是否已删除
if (drugs[drug_index].is_deleted == 1) {
printf("该药品已被删除!\n");
return;
}
// 输入购买数量
int quantity;
printf("请输入购买数量: ");
if (scanf("%d", &quantity) != 1) {
printf("输入无效!\n");
clear_input_buffer();
return;
}
clear_input_buffer();
if (quantity <= 0) {
printf("数量必须大于0!\n");
return;
}
if (quantity > drugs[drug_index].stock) {
printf("库存不足!当前库存: %d\n", drugs[drug_index].stock);
return;
}
// 计算消费金额
float amount = quantity * drugs[drug_index].price;
// 更新库存和病人消费总额
drugs[drug_index].stock -= quantity;
current->total_consume += amount;
printf("\n消费成功!\n");
format_print("病人", current->name, 8);
format_print("病人ID", current->id, 8);
format_print("药品", drugs[drug_index].name, 8);
format_print_int("数量", quantity, 8);
format_print_float("单价", drugs[drug_index].price, 8);
format_print_float("消费金额", amount, 8);
format_print_float("病人总消费", current->total_consume, 8);
format_print_int("药品剩余库存", drugs[drug_index].stock, 8);
}
// 查找药品
Drug* find_drug_by_name(const char* name, int include_deleted) {
for (int i = 0; i < drug_count; i++) {
if (strcmp(drugs[i].name, name) == 0) {
if (include_deleted || drugs[i].is_deleted == 0) {
return &drugs[i];
}
}
}
return NULL;
}
// 药品管理主函数
void drug_management() {
int choice;
do {
show_drug_menu();
printf("请选择: ");
if (scanf("%d", &choice) != 1) {
printf("输入无效,请输入数字!\n");
clear_input_buffer();
continue;
}
clear_input_buffer();
switch (choice) {
case 1:
list_all_drugs();
break;
case 2:
add_new_drug();
break;
case 3:
modify_drug();
break;
case 4:
delete_drug();
break;
case 5:
export_drug_list();
break;
case 6:
return;
default:
printf("无效的选择,请重新输入!\n");
}
if (choice != 6) {
printf("\n按回车键继续...");
getchar();
}
} while (choice != 6);
}
// 显示所有药品
void list_all_drugs() {
printf("\n=== 所有药品信息 ===\n");
printf("药品名称 库存数量 单价(元) 状态\n");
printf("------------------------------------------------\n");
for (int i = 0; i < drug_count; i++) {
printf("%-20s%-12d%-12.2f%s\n",
drugs[i].name,
drugs[i].stock,
drugs[i].price,
drugs[i].is_deleted ? "已删除" : "正常");
}
printf("------------------------------------------------\n");
printf("总共 %d 种药品\n", drug_count);
}
// 添加新药品
void add_new_drug() {
if (drug_count >= MAX_DRUGS) {
printf("药品数量已达上限,无法添加!\n");
return;
}
char name[MAX_DRUG_NAME_LEN];
printf("\n请输入药品名称: ");
fgets(name, MAX_DRUG_NAME_LEN, stdin);
// 去除末尾的换行符
size_t len = strlen(name);
if (len > 0 && name[len - 1] == '\n') {
name[len - 1] = '\0';
}
// 检查药品是否已存在
Drug* existing_drug = find_drug_by_name(name, 1);
if (existing_drug != NULL) {
if (existing_drug->is_deleted == 1) {
// 恢复已删除的药品
printf("该药品已存在(已被删除),是否恢复?(y/n): ");
char confirm;
scanf("%c", &confirm);
clear_input_buffer();
if (confirm == 'y' || confirm == 'Y') {
existing_drug->is_deleted = 0;
int stock;
printf("请输入要增加的库存数量: ");
scanf("%d", &stock);
clear_input_buffer();
existing_drug->stock += stock;
printf("药品已恢复,库存增加成功!\n");
format_print("药品名称", existing_drug->name, 12);
format_print_int("当前库存", existing_drug->stock, 12);
}
return;
}
// 如果药品存在且未删除,则增加库存
printf("该药品已存在,当前库存: %d\n", existing_drug->stock);
printf("请输入要增加的库存数量: ");
int stock;
scanf("%d", &stock);
clear_input_buffer();
existing_drug->stock += stock;
printf("库存增加成功!\n");
format_print("药品名称", existing_drug->name, 12);
format_print_int("当前库存", existing_drug->stock, 12);
return;
}
// 新增药品
printf("请输入库存数量: ");
int stock;
scanf("%d", &stock);
clear_input_buffer();
printf("请输入单价: ");
float price;
scanf("%f", &price);
clear_input_buffer();
if (stock < 0 || price < 0) {
printf("库存和单价不能为负数!\n");
return;
}
strcpy(drugs[drug_count].name, name);
drugs[drug_count].stock = stock;
drugs[drug_count].price = price;
drugs[drug_count].is_deleted = 0;
drug_count++;
printf("\n药品添加成功!\n");
format_print("药品名称", name, 12);
format_print_int("库存数量", stock, 12);
format_print_float("单价", price, 12);
}
// 修改药品信息
void modify_drug() {
printf("\n=== 修改药品信息 ===\n");
if (drug_count == 0) {
printf("当前没有药品信息!\n");
return;
}
char name[MAX_DRUG_NAME_LEN];
printf("请输入要修改的药品名称: ");
fgets(name, MAX_DRUG_NAME_LEN, stdin);
// 去除末尾的换行符
size_t len = strlen(name);
if (len > 0 && name[len - 1] == '\n') {
name[len - 1] = '\0';
}
Drug* drug = find_drug_by_name(name, 0);
if (drug == NULL) {
printf("未找到该药品!\n");
return;
}
printf("\n=== 修改 %s 的信息 ===\n", drug->name);
printf("当前库存: %d\n", drug->stock);
printf("当前单价: %.2f\n", drug->price);
int choice;
do {
printf("\n1. 修改库存\n");
printf("2. 修改单价\n");
printf("3. 完成修改\n");
printf("请选择要修改的项目: ");
scanf("%d", &choice);
clear_input_buffer();
switch (choice) {
case 1:
printf("请输入新的库存数量: ");
scanf("%d", &drug->stock);
clear_input_buffer();
if (drug->stock < 0) {
printf("库存不能为负数!\n");
drug->stock = 0;
}
printf("库存修改成功!\n");
break;
case 2:
printf("请输入新的单价: ");
scanf("%f", &drug->price);
clear_input_buffer();
if (drug->price < 0) {
printf("单价不能为负数!\n");
drug->price = 0;
}
printf("单价修改成功!\n");
break;
case 3:
printf("信息修改完成!\n");
break;
default:
printf("无效的选择!\n");
}
} while (choice != 3);
}
// 删除药品
void delete_drug() {
printf("\n=== 删除药品 ===\n");
if (drug_count == 0) {
printf("当前没有药品信息!\n");
return;
}
char name[MAX_DRUG_NAME_LEN];
printf("请输入要删除的药品名称: ");
fgets(name, MAX_DRUG_NAME_LEN, stdin);
// 去除末尾的换行符
size_t len = strlen(name);
if (len > 0 && name[len - 1] == '\n') {
name[len - 1] = '\0';
}
Drug* drug = find_drug_by_name(name, 0);
if (drug == NULL) {
printf("未找到该药品!\n");
return;
}
printf("确认要删除药品 %s 吗?(y/n): ", drug->name);
char confirm;
scanf("%c", &confirm);
clear_input_buffer();
if (confirm == 'y' || confirm == 'Y') {
drug->is_deleted = 1;
printf("药品 %s 已删除!\n", drug->name);
}
else {
printf("取消删除操作\n");
}
}
// 导出药品清单到文件
void export_drug_list() {
printf("\n=== 导出药品清单 ===\n");
char filename[100];
printf("请输入要导出的文件名(例如:drugs_export.txt): ");
scanf("%99s", filename);
clear_input_buffer();
FILE* file = fopen(filename, "w");
if (file == NULL) {
printf("无法创建文件!\n");
return;
}
// 写入表头
fprintf(file, "%-20s %-10s %-10s %-10s\n",
"药品名称", "库存", "单价", "状态");
fprintf(file, "-------------------------------------------------\n");
// 写入药品信息
for (int i = 0; i < drug_count; i++) {
fprintf(file, "%-20s %-10d %-10.2f %-10s\n",
drugs[i].name,
drugs[i].stock,
drugs[i].price,
drugs[i].is_deleted ? "已删除" : "正常");
}
fclose(file);
printf("药品清单已成功导出到 %s\n", filename);
}
// 保存病人数据到文件(文本格式)
void save_patient_data() {
FILE* file = fopen("patients.txt", "w");
if (file == NULL) {
printf("无法保存病人数据文件!\n");
return;
}
// 写入表头(使用固定格式)
fprintf(file, "%-10s %-10s %-4s %-12s %-8s %-32s %s\n",
"ID", "姓名", "年龄", "电话", "状态", "病历", "总消费");
// 写入每个病人的信息(固定宽度)
Patient* current = patient_head;
while (current != NULL) {
fprintf(file, "%-10s %-10s %-4d %-12s %-8s %-32s %.2f\n",
current->id,
current->name,
current->age,
current->phone,
current->is_deleted ? "已删除" : "正常",
current->record,
current->total_consume);
current = current->next;
}
fclose(file);
}
// 保存药品数据到文件(文本格式)
void save_drug_data() {
FILE* file = fopen("drugs.txt", "w");
if (file == NULL) {
printf("无法保存药品数据文件!\n");
return;
}
// 写入表头
fprintf(file, "%-20s %-10s %-10s %-10s\n",
"药品名称", "库存", "单价", "状态");
// 写入每种药品的信息
for (int i = 0; i < drug_count; i++) {
fprintf(file, "%-20s %-10d %-10.2f %-10s\n",
drugs[i].name,
drugs[i].stock,
drugs[i].price,
drugs[i].is_deleted ? "已删除" : "正常");
}
fclose(file);
}
// 从文件加载病人数据(文本格式)
void load_patient_data() {
FILE* file = fopen("patients.txt", "r");
if (file == NULL) {
printf("病人数据文件不存在,将创建新文件。\n");
return;
}
// 跳过表头
char buffer[256];
fgets(buffer, sizeof(buffer), file);
// 读取每个病人的信息
patient_head = NULL; // 清空现有数据(如果需要的话)
while (fgets(buffer, sizeof(buffer), file) != NULL) {
Patient* new_patient = (Patient*)malloc(sizeof(Patient));
if (new_patient == NULL) {
printf("内存分配失败!\n");
fclose(file);
return;
}
// 初始化
memset(new_patient, 0, sizeof(Patient));
char status[20];
// 解析数据 - 注意字段宽度
if (sscanf(buffer, "%10s %10s %d %11s %7s %32s %f",
new_patient->id,
new_patient->name,
&new_patient->age,
new_patient->phone,
status,
new_patient->record,
&new_patient->total_consume) >= 6) {
new_patient->is_deleted = (strcmp(status, "已删除") == 0);
new_patient->next = patient_head;
patient_head = new_patient;
}
else {
free(new_patient);
}
}
fclose(file);
}
// 从文件加载药品数据(文本格式)
void load_drug_data() {
FILE* file = fopen("drugs.txt", "r");
if (file == NULL) {
// 文件不存在,使用默认数据
initialize_drugs();
return;
}
// 跳过表头
char buffer[256];
fgets(buffer, sizeof(buffer), file);
// 读取每种药品的信息
drug_count = 0;
while (fgets(buffer, sizeof(buffer), file) != NULL && drug_count < MAX_DRUGS) {
char status[20];
if (sscanf(buffer, "%30s %d %f %10s",
drugs[drug_count].name,
&drugs[drug_count].stock,
&drugs[drug_count].price,
status) >= 3) {
drugs[drug_count].is_deleted = (strcmp(status, "已删除") == 0);
drug_count++;
}
}
fclose(file);
}
// 初始化药品数据(默认数据)
void initialize_drugs() {
// 初始化药品1
strcpy(drugs[0].name, "阿莫西林胶囊");
drugs[0].stock = 100;
drugs[0].price = 25.5f;
drugs[0].is_deleted = 0;
// 初始化药品2
strcpy(drugs[1].name, "布洛芬片");
drugs[1].stock = 200;
drugs[1].price = 15.0f;
drugs[1].is_deleted = 0;
// 初始化药品3
strcpy(drugs[2].name, "感冒灵颗粒");
drugs[2].stock = 150;
drugs[2].price = 18.0f;
drugs[2].is_deleted = 0;
// 初始化药品4
strcpy(drugs[3].name, "维生素C片");
drugs[3].stock = 300;
drugs[3].price = 8.5f;
drugs[3].is_deleted = 0;
drug_count = 4;
}
// 显示药品库存
void show_drug_inventory() {
printf("\n=== 药品库存信息 ===\n");
printf("药品名称 库存数量 单价(元) 总价值(元)\n");
printf("------------------------------------------------------\n");
float total_value = 0.0f;
for (int i = 0; i < drug_count; i++) {
if (drugs[i].is_deleted == 0) {
float drug_value = drugs[i].stock * drugs[i].price;
total_value += drug_value;
printf("%-20s%-12d%-12.2f%.2f\n",
drugs[i].name,
drugs[i].stock,
drugs[i].price,
drug_value);
}
}
printf("------------------------------------------------------\n");
printf("库存总价值: %.2f元\n", total_value);
// 显示库存警告
printf("\n=== 库存警告 ===\n");
int warning_count = 0;
for (int i = 0; i < drug_count; i++) {
if (drugs[i].is_deleted == 0 && drugs[i].stock < 10) {
printf("警告: %s 库存不足,仅剩 %d\n", drugs[i].name, drugs[i].stock);
warning_count++;
}
}
if (warning_count == 0) {
printf("所有药品库存充足\n");
}
}
// 清空输入缓冲区
void clear_input_buffer() {
int c;
while ((c = getchar()) != '\n' && c != EOF);
}
// 释放所有内存(病人链表)
void free_all_memory() {
Patient* current = patient_head;
while (current != NULL) {
Patient* next = current->next;
free(current);
current = next;
}
patient_head = NULL;
}
(2)病人/药品数据加载(load_patient_data()/load_drug_data())依赖固定宽度解析,文件格式稍有变动(多空格、换行、字段对齐偏差)即加载失败,容错性极差;
(3)品库存预警阈值(10)通过代码硬编码,无系统内配置入口,无法根据医院实际库存需求灵活调整预警数值;
(4)病人消费(patient_consumption())仅支持单药品单次购买,需反复进入消费界面选择药品,操作繁琐,无多药品批量消费功能;
(5)数值输入校验不足:年龄、药品库存、药品单价等输入负数时,仅简单重置为0,无明确错误提示,用户无法知晓输入异常原因;
(6)病人ID添加时,仅校验“未删除病人ID不重复”,未校验已删除病人ID,易出现重复ID(已删除病人恢复后冲突)。
四、代码修改
(1)新增全局变量及函数声明(代码顶部)
点击查看代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// 原有常量定义保持不变
#define MAX_ID_LEN 20//病人ID
#define MAX_NAME_LEN 20//病人姓名
#define MAX_RECORD_LEN 31// 病人信息改为30字+1个结尾符
#define MAX_DRUG_NAME_LEN 30//药品名称
#define MAX_DRUGS 100//新增:药品数量
#define MAX_PHONE_LEN 12// 新增:病人电话:11位数字+1个结尾符
#define ADMIN_USERNAME "admin"//新增:管理员账号
#define ADMIN_PASSWORD "admin123"//新增:管理员密码(初始默认,后续从文件读取)
// 新增:可配置库存预警阈值
int DRUG_WARNING_THRESHOLD = 10;
// 原有结构体定义保持不变
typedef struct Patient {
char id[MAX_ID_LEN];//ID
char name[MAX_NAME_LEN];//姓名
char record[MAX_RECORD_LEN];// 病历(30字以内)
char phone[MAX_PHONE_LEN];//新增:联系电话(11位数字)
int age;//新增:年龄
int is_deleted;//新增:状态(1-被删除,0-正常)
float total_consume;//总消费
struct Patient* next;//链表存储-下一个病人地址
} Patient;
typedef struct {
char name[MAX_DRUG_NAME_LEN];//药品名称
int stock;//数量
float price;//价格
int is_deleted;//状态(1-被删除,0-正常)
} Drug;
// 原有全局变量保持不变
Patient* patient_head = NULL;//病人头指针置空
Drug drugs[MAX_DRUGS];//新增:药品数组(容量100)
int drug_count = 0;//新增:药品数量
// 原有函数声明保持不变
void admin_login();
void modify_patient_info();
void delete_patient();
void restore_patient();
void list_deleted_patients();
Patient* find_patient_by_id(const char* id, int include_deleted);
void initialize_drugs();
void show_main_menu();
void show_patient_menu();
void show_drug_menu();
void add_new_patient();
void search_patient();
void list_all_patients();
void patient_consumption();
void save_patient_data();
void save_drug_data();
void load_patient_data();
void load_drug_data();
void show_drug_inventory();
void clear_input_buffer();
void free_all_memory();
void drug_management();
void add_new_drug();
void modify_drug();
void delete_drug();
void export_drug_list();
Drug* find_drug_by_name(const char* name, int include_deleted);
void list_all_drugs();
void format_print(const char* label, const char* value, int label_width);
void format_print_int(const char* label, int value, int label_width);
void format_print_float(const char* label, float value, int label_width);
int validate_phone(const char* phone);
// 新增函数声明(对应改进思路)
void set_warning_threshold();// 设置库存预警阈值
点击查看代码
// 修复后的添加新药品函数
void add_new_drug() {
if (drug_count >= MAX_DRUGS) {
printf("药品数量已达上限,无法添加!\n");
return;
}
char name[MAX_DRUG_NAME_LEN];
printf("\n请输入药品名称: ");
// 修复:先清空缓冲区,避免残留字符导致空名称
clear_input_buffer();
fgets(name, MAX_DRUG_NAME_LEN, stdin);
// 去除末尾的换行符
size_t len = strlen(name);
if (len > 0 && name[len - 1] == '\n') {
name[len - 1] = '\0';
}
// 新增:药品名称非空校验
if (strlen(name) == 0) {
printf("错误:药品名称不能为空!\n");
return;
}
// 原有逻辑:检查药品是否已存在
Drug* existing_drug = find_drug_by_name(name, 1);
if (existing_drug != NULL) {
if (existing_drug->is_deleted == 1) {
// 恢复已删除的药品
printf("该药品已存在(已被删除),是否恢复?(y/n): ");
char confirm;
scanf("%c", &confirm);
clear_input_buffer();
if (confirm == 'y' || confirm == 'Y') {
existing_drug->is_deleted = 0;
int stock;
printf("请输入要增加的库存数量: ");
scanf("%d", &stock);
clear_input_buffer();
existing_drug->stock += stock;
printf("药品已恢复,库存增加成功!\n");
format_print("药品名称", existing_drug->name, 12);
format_print_int("当前库存", existing_drug->stock, 12);
}
return;
}
// 如果药品存在且未删除,则增加库存
printf("该药品已存在,当前库存: %d\n", existing_drug->stock);
printf("请输入要增加的库存数量: ");
int stock;
scanf("%d", &stock);
clear_input_buffer();
existing_drug->stock += stock;
printf("库存增加成功!\n");
format_print("药品名称", existing_drug->name, 12);
format_print_int("当前库存", existing_drug->stock, 12);
return;
}
// 新增:库存/单价严格校验(对应问题6)
int stock;
printf("请输入库存数量: ");
while (scanf("%d", &stock) != 1 || stock < 0) {
printf("输入无效!请输入非负整数: ");
clear_input_buffer();
}
clear_input_buffer();
float price;
printf("请输入单价: ");
while (scanf("%f", &price) != 1 || price < 0) {
printf("输入无效!请输入非负数值: ");
clear_input_buffer();
}
clear_input_buffer();
// 原有新增药品逻辑保持不变
strcpy(drugs[drug_count].name, name);
drugs[drug_count].stock = stock;
drugs[drug_count].price = price;
drugs[drug_count].is_deleted = 0;
drug_count++;
printf("\n药品添加成功!\n");
format_print("药品名称", name, 12);
format_print_int("库存数量", stock, 12);
format_print_float("单价", price, 12);
}
点击查看代码
// 重构后的病人消费函数
void patient_consumption() {
printf("\n=== 病人消费 ===\n");
if (patient_head == NULL) {
printf("请先注册病人!\n");
return;
}
char patient_id[MAX_ID_LEN];
printf("请输入病人ID: ");
scanf("%19s", patient_id);
clear_input_buffer();
// 查找病人(只找未删除的)
Patient* current = find_patient_by_id(patient_id, 0);
if (current == NULL) {
printf("未找到该病人!\n");
return;
}
// 显示药品列表(过滤已删除药品)
printf("\n=== 药品列表 ===\n");
printf("编号 药品名称 库存 单价(元)\n");
printf("-----------------------------------------\n");
int valid_drug_count = 0;
int drug_index_map[MAX_DRUGS];// 映射显示编号到实际数组索引
for (int i = 0; i < drug_count; i++) {
if (drugs[i].is_deleted == 0) {
drug_index_map[valid_drug_count] = i;
printf("%-6d%-20s%-8d%.2f\n",
valid_drug_count + 1,
drugs[i].name,
drugs[i].stock,
drugs[i].price);
valid_drug_count++;
}
}
if (valid_drug_count == 0) {
printf("暂无可用药品!\n");
return;
}
// 批量选择药品逻辑
float total_amount = 0.0f;
char continue_choice = 'y';
do {
int drug_choice;
printf("\n请选择药品编号(1-%d): ", valid_drug_count);
while (scanf("%d", &drug_choice) != 1 || drug_choice < 1 || drug_choice > valid_drug_count) {
printf("输入无效!请输入1-%d之间的数字: ", valid_drug_count);
clear_input_buffer();
}
clear_input_buffer();
int drug_index = drug_index_map[drug_choice - 1];
// 购买数量校验(对应问题6)
int quantity;
printf("请输入购买数量: ");
while (scanf("%d", &quantity) != 1 || quantity <= 0) {
printf("输入无效!请输入大于0的整数: ");
clear_input_buffer();
}
clear_input_buffer();
if (quantity > drugs[drug_index].stock) {
printf("库存不足!当前库存: %d\n", drugs[drug_index].stock);
continue;
}
// 计算金额并更新库存
float amount = quantity * drugs[drug_index].price;
total_amount += amount;
drugs[drug_index].stock -= quantity;
printf("已选择:%s x %d,本次金额:%.2f元\n",
drugs[drug_index].name, quantity, amount);
// 询问是否继续选择
printf("是否继续选择其他药品?(y/n): ");
scanf("%c", &continue_choice);
clear_input_buffer();
} while (continue_choice == 'y' || continue_choice == 'Y');
// 更新病人总消费
current->total_consume += total_amount;
// 消费结果展示
printf("\n===== 消费结算 =====\n");
format_print("病人姓名", current->name, 10);
format_print("病人ID", current->id, 10);
format_print_float("本次消费总额", total_amount, 10);
format_print_float("病人累计消费", current->total_consume, 10);
}
点击查看代码
// 修改后的添加新病人函数(仅修改ID校验部分)
void add_new_patient() {
Patient* new_patient = (Patient*)malloc(sizeof(Patient));
if (new_patient == NULL) {
printf("内存分配失败!\n");
return;
}
// 初始化病人信息
memset(new_patient, 0, sizeof(Patient));
printf("\n=== 注册新病人 ===\n");
// 输入病人ID
printf("请输入病人ID: ");
scanf("%19s", new_patient->id);
clear_input_buffer();
// 修复:检查ID是否已存在(包含已删除病人,避免恢复后冲突)
Patient* current = find_patient_by_id(new_patient->id, 1);
if (current != NULL) {
printf("错误:该病人ID已存在(含已删除病人),请更换ID!\n");
free(new_patient);
return;
}
// 原有后续输入逻辑保持不变...
}
五、修改后截图
(1)修改后之前存在过的编号无法重建:
;
(2)药品批量修改:
;
(3)药品减少的阈值可以自动调整:
。
六、医院信息管理系统逆向软件设计总结
1. 难点分析
(1)代码冗余与输入缓冲区问题:在不破坏原有病人管理、药品管理核心功能的前提下,消除输入缓冲区残留导致的逻辑异常,同时精简重复的输入校验代码,提升代码可读性与可维护性。
(2)异常处理薄弱:需在保证功能流畅的同时,对年龄、库存、单价等负数输入、无效药品编号、文件读写失败等场景做精准拦截,避免程序崩溃或静默失败。
(3)链表内存管理隐患:病人链表动态分配内存的场景下,需确保程序退出时完整释放所有节点,避免内存泄漏,同时不影响正常的病人增删查改逻辑。
(4)消费功能局限:在保持系统简洁性的基础上,将单药品消费扩展为多药品批量消费,同时适配分页后的药品编号,避免功能逻辑混乱。
2. 优化代码后优点
(1)代码结构更清晰:通过统一输入校验、封装分页逻辑,消除了重复代码,核心功能模块职责更明确,便于后续维护和功能扩展。
(2)程序健壮性显著提升:完善了数值输入、文件操作、菜单选择等场景的异常拦截,给出明确错误提示,避免因无效输入或文件读写失败导致程序崩溃。
(3)内存管理更规范:优化了病人链表的释放逻辑,确保程序退出时完整回收所有动态分配的内存,彻底消除了内存泄漏隐患。
(4)消费功能更实用:实现了多药品批量消费功能,适配分页后的药品编号,操作更高效,满足用户批量结算需求。
3. 心得总结
通过对医院信息管理系统的逆向分析与代码优化,我深刻体会到,初始版本在核心业务(病人管理、药品管理、数据持久化)的实现上具备基础完整性,但在代码复用、异常防护、用户体验和系统灵活性方面存在明显短板。
本次优化过程中,我重点针对输入缓冲区残留、数值校验缺失、内存泄漏风险、消费功能局限等问题进行了改进,不仅让代码更简洁易维护,也让程序在面对异常场景时更稳定,用户操作体验得到了明显提升。同时也认识到,当前系统仍有可优化空间:比如可以进一步扩展库存预警的自动通知、病人消费报表生成等高级功能,让系统更贴合实际医院管理场景;也可以优化界面交互,增加更多引导提示,降低用户操作门槛;还可以扩展对 CSV 等其他文件格式的支持,提升数据导入导出的灵活性。
这次逆向优化不仅是对现有系统的一次升级,更让我熟练掌握了 C 语言中内存管理、异常处理、代码重构的核心技巧,为未来更复杂系统的开发与维护积累了宝贵的实践经验。