C语言 员工/图书/药品信息管理系统 综合实验作业实例
一、引子
大部分大学的程序设计与实验课程,尤其是非本专业学生,学习的都是简单的C语言课程,课程最后会有一次综合实验,主题一般是信息处理或管理系统。像我们学校,给予的主题为图书、员工和药品。内容也很简单,要求能完成数据的列表、排序、查找、筛选和修改。作者的学校也会以此为契机来举办一年一度的程序设计竞赛。这里将作者用于参赛的程序作品分享给大家,并附上代码说明,欢迎各位讨论和指出不足。
注:程序代码的总体难度不高,配上说明更易于理解;但其中有部分较难的区域已经另外标出,非本专业学生可以选择性学习。
声明
1、程序在制作过程中参考了CSDN作者@类;面向对象的代码和思路,原文链接: C语言员工信息管理系统(附源码)
2、此代码的解析教程为作者原创,转载和除学习外的其他用途请标明出处!
二、实验准备
综合实验开始前,我们要先熟读综合实验任务书,了解一下实验的主题是什么,程序需要干什么,代码要用到哪些重要语句。在这里我们统一以员工信息管理系统为例,制作出一个完美的程序。下面是作者的学校布置的参赛程序任务书,这里仅供参考,具体以读者自己的学校的任务书为准。
从任务书中可以了解到,此次实验要求我们的程序具有数据的浏览、添加、查找、修改、删除、排序和统计功能,而且可以在能力范围内添加更多功能。那么问题已经明朗了,直接开干叭~
三、代码解析
1、代码整体
我们的程序既然要能够完成数据的浏览功能,一个完美的表格必不可少!作者翻遍CSDN的相关文章和代码,经过自己的润色加工,终于制作出了一个个人认为(基于控制台的)最好看的表格:
有了完美的外观设计,内部的代码也应该完美无缺,下面让我们开始根据主菜单中的功能来一个个分析并实现它们!
2、数据浏览功能
浏览数据是整个程序中最简单的活了,只需要将文件中的数据提出来并依次列举,加上一个序号即可。下面是该部分的源代码,最终实现的结果如上图。
void list()
{
printf("\n");
printf(TABLE_LINE);
printf(TABLE_HEAD);
printf(TABLE_LINE);
for (int i = 0; i < work_sum; i++)
{
printf(TABLE_CONTENT, i + 1, works[i].id, works[i].name, works[i].sex, works[i].age, works[i].educat, works[i].salary, works[i].addr);
printf(TABLE_LINE);
}
printf(" |\033[31;43m如果显示格式错乱,请将窗口适当放大\033[0m|\n");
}
3、数据添加功能
添加数据,就是在控制台中输入一个数据体,然后把它并入到原有的结构体中,最后保存在文件里。以下是该部分源代码。
void push()
{
char id[20];
int num = -1;
char choose[6];
int checkid;
while(1)
{
while(1)
{
num = -1;
printf("\n \033[33m员工编号(输入0退出):>>\033[0m");
scanf("%s",&id);
checkid=atoi(id);
if(checkid==0)
{
goto pushout;
}
for(int i=0;i<work_sum;i++)
{
if(strcmp(works[i].id,id)==0)
{
num=i;
break;
}
}
if(num < 0 || num > work_sum)
{
break;
}
else
{
printf(" \033[31m该工号已存在,请重新输入!\033[0m\n");
getchar();
continue;
}
}
memcpy(works[work_sum].id,id,sizeof(works[work_sum].id));
printf(" \033[33m员工姓名:>>\033[0m");
scanf("%s", works[work_sum].name);
printf(" \033[33m员工性别:>>\033[0m");
scanf("%s", works[work_sum].sex);
printf(" \033[33m员工年龄:>>\033[0m");
scanf("%s", &works[work_sum].age);
printf(" \033[33m员工职位:>>\033[0m");
scanf("%s", works[work_sum].educat);
printf(" \033[33m所属部门:>>\033[0m");
scanf("%s", works[work_sum].addr);
printf(" \033[33m员工工资:>>\033[0m");
scanf("%s", works[work_sum].salary);
FILE* pFile;
pFile = fopen("WorkList.txt", "a");
fprintf(pFile, "%s %s %s %s %s %s %s\n", works[work_sum].id, works[work_sum].name, works[work_sum].sex, works[work_sum].age, works[work_sum].educat, works[work_sum].salary, works[work_sum].addr);
fclose(pFile);
pFile = NULL;
work_sum++;
}
pushout:
printf("\n \033[47;32m新员工信息已保存!\033[0m\n\n");
}
这里稍微讲解一下这一串代码中的小难点(非本专业学生需要注意,因为写的时候需要顾及所有人,但非本专业学生的课程并未涉及至此,可以依能力学习):
1、输入工号时,套用了两个while(1)的死循环,这里用于检查员工工号是否重复,重复的话就重新输入,但也要再次判断;
2、这里使用了一个小功能叫做“输入0退出”,所以还需要判断输入的是不是一个0,是的话就要跳出这两个死循环,然后结束。但是break语句一次只能跳出一个循环,但是如果需要多次输入的话第二个break是用不了的,不然就只能录入一个员工了,除非你到外面的死循环里再判断一次是不是0,但太麻烦了,所以我使用了goto语句,直接略过所有代码,直达结尾。如果看不懂或者需要使用传统方法的读者,在外循环的结尾处再加一个if语句就好了。
读者自行运行代码看样子吧,我就偷懒不截图了嘿嘿~~
4、数据查询功能
左思右想之下,我发现如果使用最基础的查找逻辑会非常麻烦,因为你需要二级菜单来判断要查啥:工号?姓名?职务?部门?…这样写不仅程序冗长,使用也不方便,这里直接分享@类;面向对象的程序思路,使用关键词搜索法,使用关键词去比对所有数据,这样不仅程序简洁明了,使用也超级方便。
void find()
{
int u=0;
char str[99] = "";
char outobj[1]={0};
char clsobj[1]={1};
u=0;
printf("\n \033[32m请输入关键词搜索:>\033[0m");
scanf("%s", str);
getchar();
printf(" \033[32m正在搜索,请稍后......\033[0m\n");
printf(TABLE_LINE);
printf(TABLE_HEAD);
printf(TABLE_LINE);
for (int i = 0; i < work_sum; i++)
{
if (!strcmp(str, works[i].name) ||
!strcmp(str, works[i].sex) ||
!strcmp(str,works[i].id) ||
!strcmp(str,works[i].age) ||
!strcmp(str,works[i].educat)||
!strcmp(str,works[i].salary)||
!strcmp(str,works[i].addr))
{
printf(TABLE_CONTENT, i+1 , works[i].id, works[i].name, works[i].sex, works[i].age, works[i].educat, works[i].salary, works[i].addr);
printf(TABLE_LINE);
u++; //计算查找到的数据总数
}
}
printf(" |\033[31;43m如果显示格式错乱,请将窗口适当放大\033[0m|\n");
printf("\n \033[47;32m共查询到%d个结果...\033[0m\n",u);
}
和原代码不一样的是,我在里面多加了一个计数器用来统计总共搜索到的结果。下面是运行结果:
5、数据修改功能
修改数据时,我们的日常使用逻辑是不是有多个框,一个个显示出各类信息,然后供使用者修改并保存?而基础方法却是使用超级多的二级菜单,问你:要修改谁的?要修改他的啥?要改成啥?保不保存?…那么,我们虽然做不出很好的修改界面,稍微次一点的总可以吧?下面是源代码(因为作者时间和水平有限,上面所说的修改方式还没写出来,等写出来了再分享吧)
//更新数据要用的函数,下面的那一个才是主体!
void update_subitem(char str[], int num)
{
int x = 0;
int y = 0;
int key = 13;
key = _getch();
x = wherex();
y = wherey();
if (key != 13)
{
int length = strlen(str);
gotoxy(x-length, y);
for (int i = 0; i < length; i++)
{
printf(" ");
}
gotoxy(x - length, y);
scanf("%s", str);
}
else
{
printf("\n");
}
}
//这是更新数据用的函数主体
void update()
{
char id[20];
int num = -1;
int x = 0;
int y = 0;
int key = 13;
printf(" \033[32m请输入员工编号修改:>\033[0m");
scanf("%s", &id);
for (int i = 0; i < work_sum; i++)
{
if (strcmp(works[i].id, id) == 0)
{
num = i;
break;
}
}
if (num < 0 || num > work_sum)
{
printf(" \033[31m抱歉,没有这个编号\033[0m\n");
return;
}
printf(" \033[32m该员工信息如下:\n\033[0m");
printf(TABLE_LINE);
printf(TABLE_HEAD);
printf(TABLE_LINE);
printf(TABLE_CONTENT, 1 , works[num].id, works[num].name, works[num].sex, works[num].age, works[num].educat, works[num].salary, works[num].addr);
printf(TABLE_LINE);
printf(" |\033[31;43m如果显示格式错乱,请将窗口适当放大\033[0m|\n");
work* index = works;
for (int i = 0; i < num; i++)
{
index++;
}
printf("\n");
printf(" \033[32m员工编号:>>\033[0m%s", works[num].id);
update_subitem(index->id, num);
printf(" \033[32m员工姓名:>>\033[0m%s", works[num].name);
update_subitem(index->name, num);
printf(" \033[32m员工性别:>>\033[0m%s", works[num].sex);
update_subitem(index->sex, num);
printf(" \033[32m员工年龄:>>\033[0m%s", works[num].age);
update_subitem(index->age, num);
printf(" \033[32m员工职位:>>\033[0m%s", works[num].educat);
update_subitem(index->educat, num);
printf(" \033[32m所属部门:>>\033[0m%s", works[num].addr);
update_subitem(index->addr, num);
printf(" \033[32m员工工资:>>\033[0m%s", works[num].salary);
update_subitem(index->salary, num);
save_info();
printf(" \033[32m修改成功!\033[0m\n");
}
最终的运行结果就是在修改之前可以直接显示出原有信息,所以可以按回车键快速跳过无需修改的地方,也算很方便了好吧~
6、数据删除功能
删除是数据处理中较为基础的一个功能了,这里不多加赘述。
//删除用的函数
void remove()
{
char id[20];
int num = -1;
char choose[6];
printf(" \033[32m请输入员工编号删除:>\033[0m");
scanf("%s", &id);
for (int i = 0; i < work_sum; i++) //查找是否存在
{
if (strcmp(works[i].id, id) == 0)
{
num = i;
break;
}
}
if (num < 0 || num > work_sum)
{
printf(" \033[31m抱歉,没有这个编号\033[0m\n");
return;
}
printf(" \033[33m该员工信息如下:\n\033[0m");
printf(TABLE_LINE);
printf(TABLE_HEAD);
printf(TABLE_LINE);
printf(TABLE_CONTENT, 1 , works[num].id, works[num].name, works[num].sex, works[num].age, works[num].educat, works[num].salary, works[num].addr);
printf(TABLE_LINE);
printf(" |\033[31;43m如果显示格式错乱,请将窗口适当放大\033[0m|\n");
printf("\n \033[32m再次确定是否删除(y/n)>>\033[0m");
scanf("%s", &choose);
if (strcmp(choose, "y") == 0 ||
strcmp(choose, "Y") == 0 ||
strcmp(choose, "yes") == 0 ||
strcmp(choose, "YES") == 0)
{
for (int i = num; i < work_sum - 1; i++) //你给一个数组删了,就得把后面的数组往上提一列,比如共15个人,删了第8个人,那么原来的第9个就变成了第8个,以此类推,就需要重新排一下号
{
works[i] = works[i + 1];
}
work_sum--;
printf(" \033[47;32m删除完毕!\033[0m\n");
save_info();
}
else
{
printf(" \033[47;32m已取消删除\n\033[0m");
}
}
7、数据筛选功能
这个部分呢,读者们自行选择观看。因为使用到了真实的窗口程序代码,建议本专业学生仔细阅读并了解一下,非本专业学生呢可以跳过了。
(1)主程序中的代码实现
在主程序中,使用system()函数调用外部程序,最后还要处理外部程序返回的数据并最终完成筛选。以下是主程序中的代码:
void selectfirst()
{
int sela[255];
int selb[255];
int selc[255];
int i=0,j;
printf(" \033[32m请在弹出的筛选框中进行操作...\n\033[0m");
system("select.exe");
//读取第1个选项:(工资or年龄)
FILE *fp;
fp = fopen("selecta.txt", "r");
if(fp == NULL)
printf("1111");
while(fscanf(fp, "%d", &sela[i]) != EOF)
i++;
fclose(fp);
fp = NULL;
//读取第2个选项:(大于or小于)
i=0;
fp = fopen("selectb.txt", "r");
if(fp == NULL)
printf("1111");
while(fscanf(fp, "%d", &selb[i]) != EOF)
{
i++;
}
fclose(fp);
fp = NULL;
//读取第3个选项:(数字是多少)
i=0;
fp = fopen("selectc.txt", "r");
if(fp == NULL)
printf("1111");
while(fscanf(fp,"%d", &selc[i])!=EOF)
{
i++;
}
fclose(fp);
fp = NULL;
if(sela[0]==1)
{
if(selb[0]==1)
{
printf("\033[32m\n 当前:工资大于%d元\n\033[0m",selc[0]);
printf(TABLE_LINE);
printf(TABLE_HEAD);
printf(TABLE_LINE);
for (int i = 0; i < work_sum; i++)
{
if (selc[0]<atoi(works[i].salary))
{
printf(TABLE_CONTENT, i+1 , works[i].id, works[i].name, works[i].sex, works[i].age, works[i].educat, works[i].salary, works[i].addr);
printf(TABLE_LINE);
}
}
printf(" |\033[31;43m如果显示格式错乱,请将窗口适当放大\033[0m|\n");
}
if(selb[0]==2)
{
printf(" \033[32m\n 当前:工资小于%d元\n\033[0m",selc[0]);
printf(TABLE_LINE);
printf(TABLE_HEAD);
printf(TABLE_LINE);
for (int i = 0; i < work_sum; i++)
{
if (selc[0]>atoi(works[i].salary))
{
printf(TABLE_CONTENT, i+1 , works[i].id, works[i].name, works[i].sex, works[i].age, works[i].educat, works[i].salary, works[i].addr);
printf(TABLE_LINE);
}
}
printf(" |\033[31;43m如果显示格式错乱,请将窗口适当放大\033[0m|\n");
}
}
if(sela[0]==2)
{
if(selb[0]==1)
{
printf(" \033[32m\n 当前:年龄大于%d岁\n\033[0m",selc[0]);
printf(TABLE_LINE);
printf(TABLE_HEAD);
printf(TABLE_LINE);
for (int i = 0; i < work_sum; i++)
{
if (selc[0]<atoi(works[i].age))
{
printf(TABLE_CONTENT, i+1 , works[i].id, works[i].name, works[i].sex, works[i].age, works[i].educat, works[i].salary, works[i].addr);
printf(TABLE_LINE);
}
}
printf(" |\033[31;43m如果显示格式错乱,请将窗口适当放大\033[0m|\n");
}
if(selb[0]==2)
{
printf(" \033[32m\n 当前:年龄小于%d岁\n\033[0m",selc[0]);
printf(TABLE_LINE);
printf(TABLE_HEAD);
printf(TABLE_LINE);
for (int i = 0; i < work_sum; i++)
{
if (selc[0]>atoi(works[i].age))
{
printf(TABLE_CONTENT, i+1 , works[i].id, works[i].name, works[i].sex, works[i].age, works[i].educat, works[i].salary, works[i].addr);
printf(TABLE_LINE);
}
}
printf(" |\033[31;43m如果显示格式错乱,请将窗口适当放大\033[0m|\n");
}
}
}
这里我偷了一点小懒,仔细的读者不难看出,我的筛选结果分别存在了三个文件中,因为我没定义结构体而且有点懒,就用了笨方法。这种行为在以后的实际操作中不可取,因为容易造成程序冗长。定义一个结构体是最好的方法。
(2)筛选窗口的代码实现
根据上面的代码,我们重新做一个名为“select.exe”程序作为外部程序,来辅助主程序完成选择筛选条件的工作。以下是源代码:
// 使用VS可能要将下面这行代码打开
//#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE hInst;
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
PSTR szCmdLine,
int iCmdShow
){
static TCHAR szClassName[] = TEXT("Win32Demo");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
hInst = hInstance;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
wndclass.hbrBackground= (HBRUSH) GetSysColorBrush(COLOR_BTNFACE);
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName= szClassName;
RegisterClass(&wndclass);
hwnd = CreateWindow(
szClassName,
TEXT("筛选框"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
400,
250,
NULL,
NULL,
hInstance,
NULL
);
ShowWindow (hwnd, iCmdShow);
UpdateWindow (hwnd);
while( GetMessage(&msg, NULL, 0, 0) ){
TranslateMessage(&msg);
DispatchMessage (&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
FILE* pFile;
int sa,sb,sc;
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
static TCHAR szBufWage[10];
static TCHAR szBufSize[10];
static TCHAR szBufSubmit[100];
static HFONT hFont;
static HWND labobj;
static HWND radiowage;
static HWND radioage;
static HWND labsize;
static HWND radiomore;
static HWND radioless;
static HWND radioSecrecy;
static HWND labline;
static HWND btnSubmit;
static HWND hnumber;
TCHAR szNumber[100];
switch (message){
case WM_CREATE:
hFont = CreateFont(-14, -7, 0, 0, 400,
FALSE, FALSE, FALSE,DEFAULT_CHARSET,
OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY,
FF_DONTCARE, TEXT("微软雅黑")
);
labobj = CreateWindow(
TEXT("static"), TEXT("筛选项目:"),
WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE | SS_RIGHT,
20, 10, 80, 26,
hWnd, (HMENU)1, hInst, NULL
);
//定义一个选择框,边上标注了“工资”二字
radiowage = CreateWindow(
TEXT("button"), TEXT("工资"),
WS_CHILD | WS_VISIBLE | BS_LEFT| BS_AUTORADIOBUTTON | WS_GROUP,
105, 10, 50, 26,
hWnd, (HMENU)2, hInst, NULL
);
radioage = CreateWindow(
TEXT("button"), TEXT("年龄"),
WS_CHILD | WS_VISIBLE | BS_LEFT | BS_AUTORADIOBUTTON,
160, 10, 50, 26,
hWnd, (HMENU)3, hInst, NULL
);
labsize = CreateWindow(
TEXT("static"), TEXT("筛选逻辑:"),
WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE | SS_RIGHT,
20, 40, 80, 26,
hWnd, (HMENU)4, hInst, NULL
);
radiomore = CreateWindow(
TEXT("button"), TEXT("大于"),
WS_CHILD | WS_VISIBLE | BS_LEFT | BS_AUTORADIOBUTTON | WS_GROUP,
105, 40, 65, 26,
hWnd, (HMENU)5, hInst, NULL
);
radioless = CreateWindow(
TEXT("button"), TEXT("小于"),
WS_CHILD | WS_VISIBLE | BS_LEFT | BS_AUTORADIOBUTTON,
160, 40, 65, 26,
hWnd, (HMENU)6, hInst, NULL
);
labline = CreateWindow(
TEXT("static"), TEXT("筛选线(数值):"),
WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE | SS_RIGHT,
22, 70, 120, 26,
hWnd, (HMENU)7, hInst, NULL
);
hnumber = CreateWindow(TEXT("edit"), TEXT(""),
WS_CHILD | WS_VISIBLE | WS_BORDER| ES_AUTOHSCROLL,
160, 66, 100, 26,
hWnd, (HMENU)8, hInst, NULL
);
btnSubmit = CreateWindow(TEXT("button"), TEXT("提交"),
WS_CHILD | WS_VISIBLE | WS_BORDER | BS_FLAT,
135, 150, 100, 36,
hWnd, (HMENU)9, hInst, NULL
);
SendMessage(labobj, WM_SETFONT, (WPARAM)hFont, NULL);
SendMessage(radiowage, WM_SETFONT, (WPARAM)hFont, NULL);
SendMessage(radioage, WM_SETFONT, (WPARAM)hFont, NULL);
SendMessage(labsize, WM_SETFONT, (WPARAM)hFont, NULL);
SendMessage(radiomore, WM_SETFONT, (WPARAM)hFont, NULL);
SendMessage(radioless, WM_SETFONT, (WPARAM)hFont, NULL);
SendMessage(labline, WM_SETFONT, (WPARAM)hFont, NULL);
SendMessage(btnSubmit, WM_SETFONT, (WPARAM)hFont, NULL);
SendMessage(hnumber, WM_SETFONT, (WPARAM)hFont, NULL);
break;
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
if( wmEvent== BN_CLICKED){
switch (wmId){
case 2:
GetWindowText((HWND)lParam, szBufWage, 10);
sa=1;
pFile = fopen("selecta.txt", "w");
fprintf(pFile, "%d", sa);
fclose(pFile);
pFile = NULL;
break;
case 3:
GetWindowText((HWND)lParam, szBufWage, 10);
sa=2;
pFile = fopen("selecta.txt", "w");
fprintf(pFile, "%d", sa);
fclose(pFile);
pFile = NULL;
break;
case 5:
GetWindowText((HWND)lParam, szBufSize, 10);
sb=1;
pFile = fopen("selectb.txt", "w");
fprintf(pFile, "%d", sb);
fclose(pFile);
pFile = NULL;
break;
case 6:
GetWindowText((HWND)lParam, szBufSize, 10);
sb=2;
pFile = fopen("selectb.txt", "w");
fprintf(pFile, "%d", sb);
fclose(pFile);
pFile = NULL;
break;
case 9:
GetWindowText(hnumber, szNumber, 100);
sc=atoi(szNumber);
wsprintf(szBufSubmit, TEXT("您的筛选范围是:%s%s%s\r\n若条件无误请自行关闭筛选框"), szBufWage, szBufSize, szNumber);
MessageBox(hWnd, szBufSubmit, TEXT("筛选条件确认"), MB_ICONINFORMATION);
pFile = fopen("selectc.txt", "w");
fprintf(pFile, "%d", sc);
fclose(pFile);
pFile = NULL;
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
DeleteObject(hFont);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
为什么这个东西和主程序不能写在一起?不难看出,这个程序的主函数叫做WinAPI WinMain(),里面还有四个形参,分别是HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR szCmdLine,int iCmdShow。这四个东西的意思不在多加赘述,感兴趣的读者可以去看这篇文章:一个最简单的窗口函数的解释 WinMain。注意一点,这是一个最简单的窗口函数,不是所有的都长这样!它的主函数和我们的主程序的主函数不一样,而一个程序只能有一个主函数,因此要分开写(至少我没捣鼓出来怎么把他们俩放一块儿)。
程序运行结果如下:
8、程序中的其它模块
这一块就是那些程序中不算很重点但必须要有的函数模块,就不再过多讲解了,读者自行阅读就好。
(1)定义的头文件、结构体、变量以及一些声明
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <conio.h>
#include <Windows.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#define TABLE_LINE " +-------------------------------------------------------------------------------------------------+\n"
#define TABLE_HEAD " | \033[46;35mNO \033[0m | \033[46;35m 编号 \033[0m | \033[46;35m 姓名 \033[0m | \033[46;35m性别\033[0m | \033[46;35m年龄\033[0m | \033[46;35m 职位 \033[0m | \033[46;35m 工资 \033[0m | \033[46;35m 所属部门 \033[0m |\n"
#define TABLE_CONTENT " | %03d | %-14s | %-10s | %-4s | %-4s | %-10s | %-11s | %-18s | \n"
#define WORK_NUM 999
int work_sum = 0;
char check[20]={0};
struct work
{
char id[20];
char name[20];
char sex[8];
char age[5];
char educat[30];
char addr[90];
char salary[20];
};
work works[WORK_NUM];
(2)菜单
void menu()
{
printf("\n");
printf(" +----------------------\033[47;31m*员工管理系统*\033[m----------------------+\n");
printf(" | \t\t\t\t\t\t\t |\n");
printf(" | \t\t\t\033[36m1.浏览员工信息\033[0m\t\t\t |\n");
printf(" | \t\t\t\033[36m2.录入员工信息\033[0m\t\t\t |\n");
printf(" | \t\t\t\033[36m3.查询员工信息\033[0m\t\t\t |\n");
printf(" | \t\t\t\033[36m4.修改员工信息\033[0m\t\t\t |\n");
printf(" | \t\t\t\033[36m5.删除员工信息\033[0m\t\t\t |\n");
printf(" | \t\t\t\033[36m6.筛选员工信息\033[0m\t\t\t |\n");
printf(" | \t\t\t\033[36m7.清空屏幕信息\033[0m\t\t\t |\n");
printf(" | \t\t\t\033[36m0.退出员工管理\033[0m\t\t\t |\n");
printf(" | \t\t\t\t\t\t\t |\n");
printf(" +----------------------------------------------------------+\n");
}
(3)清理屏幕信息
//清屏
void clear_info()
{
system("cls");
menu();
}
(4)打开数据文件并将数据读取到内存
//读取文件中的所有数据到内存中来以方便使用
void init_info()
{
work_sum = 0;
FILE* fp = fopen("WorkList.txt", "a+");
if (NULL == fp)
{
return;
}
while (!feof(fp))
{
fscanf(fp, "%s %s %s %s %s %s %s\n", works[work_sum].id, works[work_sum].name, works[work_sum].sex, works[work_sum].age, works[work_sum].educat, works[work_sum].salary, works[work_sum].addr);
work_sum++;
}
fclose(fp);
fp = NULL;
}
(5)将更改后的数据保存到文件中
//保存内存中的数据到文件里
void save_info()
{
FILE* pFile;
pFile = fopen("WorkList.txt", "w+");
for (int i = 0; i < work_sum; i++)
{
fprintf(pFile, "%s %s %s %s %s %s %s\n", works[i].id, works[i].name, works[i].sex, works[i].age, works[i].educat, works[i].salary, works[i].addr);
}
fclose(pFile);
pFile = NULL;
}
(6)主函数
//主函数
int main()
{
int choose;
before();
getchar();
printf("加载中,请稍后......");
init_info();
clear_info();
while (true)
{
choose = 0;
printf("\n \033[32m请选择您的操作:>\033[0m");
scanf("%d", &choose);
getchar();
switch (choose)
{
case 1:list(); break;
case 2:push(); break;
case 3:find(); break;
case 4:update(); break;
case 5:remove(); break;
case 6:selectfirst();break;
case 7:clear_info(); break;
case 0:MessageBox(NULL,("欢迎下次使用!"),("退出"),MB_OK|MB_ICONINFORMATION); system("cls"); printf("程序已停止运行\n"); exit(0); break;
default:printf(" \033[32m抱歉,没有这个选项,回车后重新输入\033[0m\n"); getchar(); clear_info(); break;
}
}
return 0;
}
四、完整代码
//程序在Dev-C++ 5.11上运行正常
// 使用VS可能要将下面这行代码打开
//#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h> //定义头文件
#include <math.h>
#include <stdlib.h>
#include <conio.h>
#include <Windows.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#define TABLE_LINE " +-------------------------------------------------------------------------------------------------+\n"
#define TABLE_HEAD " | \033[46;35mNO \033[0m | \033[46;35m 编号 \033[0m | \033[46;35m 姓名 \033[0m | \033[46;35m性别\033[0m | \033[46;35m年龄\033[0m | \033[46;35m 职位 \033[0m | \033[46;35m 工资 \033[0m | \033[46;35m 所属部门 \033[0m |\n"
#define TABLE_CONTENT " | %03d | %-14s | %-10s | %-4s | %-4s | %-10s | %-11s | %-18s | \n"
#define WORK_NUM 999
int work_sum = 0;
char check[20]={0};
struct work
{
char id[20];
char name[20];
char sex[8];
char age[5];
char educat[30];
char addr[90];
char salary[20];
};
work works[WORK_NUM];
void gotoxy(int x, int y)
{
COORD pos = { x,y };
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hOut, pos);
}
int wherex()
{
CONSOLE_SCREEN_BUFFER_INFO pBuffer;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &pBuffer);
return (pBuffer.dwCursorPosition.X);
}
int wherey()
{
CONSOLE_SCREEN_BUFFER_INFO pBuffer;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &pBuffer);
return (pBuffer.dwCursorPosition.Y);
}
//菜单
void menu()
{
printf("\n");
printf(" +----------------------\033[47;31m*员工管理系统*\033[m----------------------+\n");
printf(" | \t\t\t\t\t\t\t |\n");
printf(" | \t\t\t\033[36m1.浏览员工信息\033[0m\t\t\t |\n");
printf(" | \t\t\t\033[36m2.录入员工信息\033[0m\t\t\t |\n");
printf(" | \t\t\t\033[36m3.查询员工信息\033[0m\t\t\t |\n");
printf(" | \t\t\t\033[36m4.修改员工信息\033[0m\t\t\t |\n");
printf(" | \t\t\t\033[36m5.删除员工信息\033[0m\t\t\t |\n");
printf(" | \t\t\t\033[36m6.筛选员工信息\033[0m\t\t\t |\n");
printf(" | \t\t\t\033[36m7.清空屏幕信息\033[0m\t\t\t |\n");
printf(" | \t\t\t\033[36m0.退出员工管理\033[0m\t\t\t |\n");
printf(" | \t\t\t\t\t\t\t |\n");
printf(" +----------------------------------------------------------+\n");
}
//清屏
void clear_info()
{
system("cls");
menu();
}
//读取文件中的所有数据到内存中来以方便使用
void init_info()
{
work_sum = 0;
FILE* fp = fopen("WorkList.txt", "a+");
if (NULL == fp)
{
return;
}
while (!feof(fp))
{
fscanf(fp, "%s %s %s %s %s %s %s\n", works[work_sum].id, works[work_sum].name, works[work_sum].sex, works[work_sum].age, works[work_sum].educat, works[work_sum].salary, works[work_sum].addr);
work_sum++;
}
fclose(fp);
fp = NULL;
}
//保存内存中的数据到文件里
void save_info()
{
FILE* pFile;
pFile = fopen("WorkList.txt", "w+");
for (int i = 0; i < work_sum; i++)
{
fprintf(pFile, "%s %s %s %s %s %s %s\n", works[i].id, works[i].name, works[i].sex, works[i].age, works[i].educat, works[i].salary, works[i].addr);
}
fclose(pFile);
pFile = NULL;
}
//新录入员工
void push()
{
char id[20];
int num = -1;
char choose[6];
int checkid;
while(1)
{
while(1)
{
num = -1;
printf("\n \033[33m员工编号(输入0退出):>>\033[0m");
scanf("%s",&id);
checkid=atoi(id);
if(checkid==0)
{
goto pushout;
}
for(int i=0;i<work_sum;i++)
{
if(strcmp(works[i].id,id)==0)
{
num=i;
break;
}
}
if(num < 0 || num > work_sum)
{
break;
}
else
{
printf(" \033[31m该工号已存在,请重新输入!\033[0m\n");
getchar();
continue;
}
}
memcpy(works[work_sum].id,id,sizeof(works[work_sum].id));
printf(" \033[33m员工姓名:>>\033[0m");
scanf("%s", works[work_sum].name);
printf(" \033[33m员工性别:>>\033[0m");
scanf("%s", works[work_sum].sex);
printf(" \033[33m员工年龄:>>\033[0m");
scanf("%s", &works[work_sum].age);
printf(" \033[33m员工职位:>>\033[0m");
scanf("%s", works[work_sum].educat);
printf(" \033[33m所属部门:>>\033[0m");
scanf("%s", works[work_sum].addr);
printf(" \033[33m员工工资:>>\033[0m");
scanf("%s", works[work_sum].salary);
FILE* pFile;
pFile = fopen("WorkList.txt", "a");
fprintf(pFile, "%s %s %s %s %s %s %s\n", works[work_sum].id, works[work_sum].name, works[work_sum].sex, works[work_sum].age, works[work_sum].educat, works[work_sum].salary, works[work_sum].addr);
fclose(pFile);
pFile = NULL;
work_sum++;
}
pushout:
printf("\n \033[47;32m新员工信息已保存!\033[0m\n\n");
}
//列表用的函数
void list()
{
printf("\n");
printf(TABLE_LINE);
printf(TABLE_HEAD);
printf(TABLE_LINE);
for (int i = 0; i < work_sum; i++)
{
printf(TABLE_CONTENT, i + 1, works[i].id, works[i].name, works[i].sex, works[i].age, works[i].educat, works[i].salary, works[i].addr);
printf(TABLE_LINE);
}
printf(" |\033[31;43m如果显示格式错乱,请将窗口适当放大\033[0m|\n");
}
//筛选数据用的函数
void selectfirst()
{
int sela[255];
int selb[255];
int selc[255];
int i=0,j;
printf(" \033[32m请在弹出的筛选框中进行操作...\n\033[0m");
system("select.exe");
//读取第1个选项:(工资or年龄)
FILE *fp;
fp = fopen("selecta.txt", "r");
if(fp == NULL)
printf("1111");
while(fscanf(fp, "%d", &sela[i]) != EOF)
i++;
fclose(fp);
fp = NULL;
//读取第2个选项:(大于or小于)
i=0;
fp = fopen("selectb.txt", "r");
if(fp == NULL)
printf("1111");
while(fscanf(fp, "%d", &selb[i]) != EOF)
{
i++;
}
fclose(fp);
fp = NULL;
//读取第3个选项:(数字是多少)
i=0;
fp = fopen("selectc.txt", "r");
if(fp == NULL)
printf("1111");
while(fscanf(fp,"%d", &selc[i])!=EOF)
{
i++;
}
fclose(fp);
fp = NULL;
if(sela[0]==1)
{
if(selb[0]==1)
{
printf("\033[32m\n 当前:工资大于%d元\n\033[0m",selc[0]);
printf(TABLE_LINE);
printf(TABLE_HEAD);
printf(TABLE_LINE);
for (int i = 0; i < work_sum; i++)
{
if (selc[0]<atoi(works[i].salary))
{
printf(TABLE_CONTENT, i+1 , works[i].id, works[i].name, works[i].sex, works[i].age, works[i].educat, works[i].salary, works[i].addr);
printf(TABLE_LINE);
}
}
printf(" |\033[31;43m如果显示格式错乱,请将窗口适当放大\033[0m|\n");
}
if(selb[0]==2)
{
printf(" \033[32m\n 当前:工资小于%d元\n\033[0m",selc[0]);
printf(TABLE_LINE);
printf(TABLE_HEAD);
printf(TABLE_LINE);
for (int i = 0; i < work_sum; i++)
{
if (selc[0]>atoi(works[i].salary))
{
printf(TABLE_CONTENT, i+1 , works[i].id, works[i].name, works[i].sex, works[i].age, works[i].educat, works[i].salary, works[i].addr);
printf(TABLE_LINE);
}
}
printf(" |\033[31;43m如果显示格式错乱,请将窗口适当放大\033[0m|\n");
}
}
if(sela[0]==2)
{
if(selb[0]==1)
{
printf(" \033[32m\n 当前:年龄大于%d岁\n\033[0m",selc[0]);
printf(TABLE_LINE);
printf(TABLE_HEAD);
printf(TABLE_LINE);
for (int i = 0; i < work_sum; i++)
{
if (selc[0]<atoi(works[i].age))
{
printf(TABLE_CONTENT, i+1 , works[i].id, works[i].name, works[i].sex, works[i].age, works[i].educat, works[i].salary, works[i].addr);
printf(TABLE_LINE);
}
}
printf(" |\033[31;43m如果显示格式错乱,请将窗口适当放大\033[0m|\n");
}
if(selb[0]==2)
{
printf(" \033[32m\n 当前:年龄小于%d岁\n\033[0m",selc[0]);
printf(TABLE_LINE);
printf(TABLE_HEAD);
printf(TABLE_LINE);
for (int i = 0; i < work_sum; i++)
{
if (selc[0]>atoi(works[i].age))
{
printf(TABLE_CONTENT, i+1 , works[i].id, works[i].name, works[i].sex, works[i].age, works[i].educat, works[i].salary, works[i].addr);
printf(TABLE_LINE);
}
}
printf(" |\033[31;43m如果显示格式错乱,请将窗口适当放大\033[0m|\n");
}
}
}
//查找用的函数
void find()
{
int u=0;
char str[99] = "";
char outobj[1]={0};
char clsobj[1]={1};
u=0;
printf("\n \033[32m请输入关键词搜索:>\033[0m");
scanf("%s", str);
getchar();
printf(" \033[32m正在搜索,请稍后......\033[0m\n");
printf(TABLE_LINE);
printf(TABLE_HEAD);
printf(TABLE_LINE);
for (int i = 0; i < work_sum; i++)
{
if (!strcmp(str, works[i].name) ||
!strcmp(str, works[i].sex) ||
!strcmp(str,works[i].id) ||
!strcmp(str,works[i].age) ||
!strcmp(str,works[i].educat)||
!strcmp(str,works[i].salary)||
!strcmp(str,works[i].addr))
{
printf(TABLE_CONTENT, i+1 , works[i].id, works[i].name, works[i].sex, works[i].age, works[i].educat, works[i].salary, works[i].addr);
printf(TABLE_LINE);
u++;
}
}
printf(" |\033[31;43m如果显示格式错乱,请将窗口适当放大\033[0m|\n");
printf("\n \033[47;32m共查询到%d个结果...\033[0m\n",u);
}
void update_subitem(char str[], int num)
{
int x = 0;
int y = 0;
int key = 13;
key = _getch();
x = wherex();
y = wherey();
if (key != 13)
{
int length = strlen(str);
gotoxy(x-length, y);
for (int i = 0; i < length; i++)
{
printf(" ");
}
gotoxy(x - length, y);
scanf("%s", str);
}
//key=13的话,那就证明按下了回车键,说明这个数据不用改
else
{
printf("\n"); //回车。这个不用改,那就直接跳过,原本是啥就是啥
}
}
//这是更新数据用的函数主体
void update()
{
char id[20];
int num = -1;
int x = 0;
int y = 0;
int key = 13;
printf("\n \033[33m请输入员工编号修改:>\033[0m");
scanf("%s", &id);
for (int i = 0; i < work_sum; i++) //查找工号是否存在
{
if (strcmp(works[i].id, id) == 0)
{
num = i;
break;
}
}
if (num < 0 || num > work_sum)
{
printf(" \033[31m抱歉,没有这个编号\033[0m\n");
return;
}
printf(" \033[32m该员工信息如下:\n\033[0m");
printf(TABLE_LINE);
printf(TABLE_HEAD);
printf(TABLE_LINE);
printf(TABLE_CONTENT, 1 , works[num].id, works[num].name, works[num].sex, works[num].age, works[num].educat, works[num].salary, works[num].addr);
printf(TABLE_LINE);
printf(" |\033[31;43m如果显示格式错乱,请将窗口适当放大\033[0m|\n");
work* index = works; //把指针index指向结构体works
for (int i = 0; i < num; i++)
{
index++;
}
printf("\n \033[33m开始修改,在编号栏输入0则为不修改\033[0m\n");
printf(" \033[32m员工编号:>>\033[0m%s", works[num].id);
update_subitem(index->id, num); //这个东西是上面定义了的更新数据用的函数
//“->”的意思是将指针index指向的结构体works中的数据项id取出来
//刚刚定义过,这个函数有两个形参,第一个参数是一个数组str[]
//这句话的意思就是 char str[]=works[num].id
printf(" \033[32m员工姓名:>>\033[0m%s", works[num].name);
update_subitem(index->name, num);
printf(" \033[32m员工性别:>>\033[0m%s", works[num].sex);
update_subitem(index->sex, num);
printf(" \033[32m员工年龄:>>\033[0m%s", works[num].age);
update_subitem(index->age, num);
printf(" \033[32m员工职位:>>\033[0m%s", works[num].educat);
update_subitem(index->educat, num);
printf(" \033[32m所属部门:>>\033[0m%s", works[num].addr);
update_subitem(index->addr, num);
printf(" \033[32m员工工资:>>\033[0m%s", works[num].salary);
update_subitem(index->salary, num);
//这里的数据更新是怎么完成的?在上一个函数里面,要更改的数据重新输入后存在了str[]里面,那个是形参,在这里它的实参是index指向的数组id,name,age等等,因此在重新输入的同时数据就已经更改好了,scanf会覆盖原有的数据不是吗?
save_info(); //把修改完的信息重新保存好
printf("\n \033[47;32m修改成功!\033[0m\n");
}
//删除用的函数
//这个没什么好说的,很基础,找到员工,列出数据,确认删除,删除之后再重新保存数据
void remove()
{
char id[20];
int num = -1;
char choose[6];
printf(" \033[32m请输入员工编号删除:>\033[0m");
scanf("%s", &id);
for (int i = 0; i < work_sum; i++) //查找是否存在
{
if (strcmp(works[i].id, id) == 0)
{
num = i;
break;
}
}
if (num < 0 || num > work_sum)
{
printf(" \033[31m抱歉,没有这个编号\033[0m\n");
return;
}
printf(" \033[33m该员工信息如下:\n\033[0m");
printf(TABLE_LINE);
printf(TABLE_HEAD);
printf(TABLE_LINE);
printf(TABLE_CONTENT, 1 , works[num].id, works[num].name, works[num].sex, works[num].age, works[num].educat, works[num].salary, works[num].addr);
printf(TABLE_LINE);
printf(" |\033[31;43m如果显示格式错乱,请将窗口适当放大\033[0m|\n");
printf("\n \033[32m再次确定是否删除(y/n)>>\033[0m");
scanf("%s", &choose);
if (strcmp(choose, "y") == 0 ||
strcmp(choose, "Y") == 0 ||
strcmp(choose, "yes") == 0 ||
strcmp(choose, "YES") == 0)
{
for (int i = num; i < work_sum - 1; i++) //你给一个数组删了,就得把后面的数组往上提一列,比如共15个人,删了第8个人,那么原来的第9个就变成了第8个,以此类推,就需要重新排一下号
{
works[i] = works[i + 1];
}
work_sum--;
printf(" \033[47;32m删除完毕!\033[0m\n");
save_info();
}
else
{
printf(" \033[47;32m已取消删除\n\033[0m");
}
}
//主函数
int main()
{
int choose;
printf("加载中,请稍后......");
init_info();
clear_info();
while (true)
{
choose = 0;
printf("\n \033[32m请选择您的操作:>\033[0m");
scanf("%d", &choose);
getchar();
switch (choose)
{
case 1:list(); break;
case 2:push(); break;
case 3:find(); break;
case 4:update(); break;
case 5:remove(); break;
case 6:selectfirst();break;
case 7:clear_info(); break;
case 0:MessageBox(NULL,("欢迎下次使用!"),("退出"),MB_OK|MB_ICONINFORMATION); system("cls"); printf("程序已停止运行\n"); exit(0); break;
default:printf(" \033[32m抱歉,没有这个选项,回车后重新输入\033[0m\n"); getchar(); clear_info(); break;
}
}
return 0;
}
五、最后
①程序在制作过程中参考了CSDN作者@类;面向对象的代码和思路,原文链接: C语言员工信息管理系统(附源码)
②此代码的解析教程为作者原创,转载和除学习外的其他用途请标明出处!
This article is written by Sleaf at ZZULI on June 15, 2024,published June 20.
All rights reserved.