程序設計題:大學生考勤系統
1 問題描述
該系統要求實現一個簡單、實用的學生考勤系統程序,主要功能包括考勤數據的新增、刪除、修改、查找、統計、排序、輸出等。所有統計數據都要利用文件系統保存,以備系統下次運行時使用。通過此課題,熟練掌握文件、數組、指針的各種操作,以及一些基本算法的應用。
2 功能要求
代碼要能提供以下幾個基本功能。
(1)考勤數據是由多條數據記錄構成,其信息包括:學號(唯一)、姓名、班級、考勤日期(由月和日組成)等等。
(2)數據保存形式:所有信息要以文本或二進制文件保存。
(3)需要實現的功能
a 新增學生信息數據(避免重復記錄,若在,不可再增加)。
b 查找數據(可按學號、姓名、考勤日期等查找)。
c 修改學生記錄(可以先查找,再修改)。
d 刪除學生記錄(請參考上面修改的處理)。
e 顯示學生考勤數據列表(可顯示部分或全部)。
f 排序(可按學號、姓名、考勤日期等排序)。
g 數據統計(對考勤數據統計分析,統計完成后,可以通過考勤系統中得出考勤明細表,考勤日報表、考勤匯總表、考勤異常表,請假匯總表等)。
h 保存數據到文件中并從文件中讀入數據。
(4)界面功能要求:
1) 采用友好的界面,實現一個功能控制菜單。
2) 每次操作都從該菜單選擇,利用循環結構使得一次運行程序可進行多次操作。
備注:
(1)數據完整性保證。例如:學號和姓名必須嚴格、規范。
(2)要求報表格式靈活、形式多樣、內容清楚,具體請學生自己構思、設計。
(3)考勤符號:出 勤√ 曠 課 X 事 假 △ 病 假 ○ 遲 到 + 早 退 –
(4)考勤時間段不少于十條。
3 其他要求
(1)界面美觀,交互方便。
(2)注釋詳細:每個函數有注釋說明功能,對參數、返回值也要以注釋的形式說明用途;關鍵的語句段要求有注釋解釋。
(3)程序的層次清晰,可讀性強。
(4)變量、函數命名符合規范。
(5)如有可能,可使用MFC 等開發工具,實現彩色或圖形操作界面。
4 開發環境
可以選擇TC2.0、TC3.0、VC++6.0、vs2010等開發環境,或者與老師討論,選擇自己熟悉的開發工具與平臺。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
|
#include<stdio.h> #include<string.h> #include<stdlib.h> #define maxsize 15 #define maxnum 1000 typedef struct { char Student_ID[maxsize]; char Name[maxsize]; char Class_Number[maxsize]; char Attandance_date[maxsize]; char Attandance_Result[maxsize]; } student; student Students[maxnum]; int num = 0; char buf[maxsize]; /*實現每次輸入結束最后輸入回車退出的功能,更好的實現與用戶的交互 */ void wait_for_Enter() { getchar (); getchar (); } /*為了實現對日期進行排序,我的想法是對于月份,直接用atoi截取出來轉化成月份,對于天數,則需要用以下函數計算*/ int convert( char str[]) { int i,j,res = 0, cnt = 0; for (i = strlen (str) - 3; i >= 0; i--) { if (str[i] >= '0' &&str[i] <= '9' ) res *= 10, res += (str[i] - '0' ); else break ; } return res; //返回天數,也就是一月中的第幾天,例如11月5日,返回的就是5;5月23日,返回的就是23 } /*該函數實現的是把student類型變量b的所有信息賦給student類型的變量a*/ void copy(student *a,student *b) { strcpy (a->Student_ID , b->Student_ID); strcpy (a->Name , b->Name); strcpy (a->Class_Number , b->Class_Number); strcpy (a->Attandance_Result , b->Attandance_Result); strcpy (a->Attandance_date , b->Attandance_date); } /*此函數的作用是實現對已存在的學生不可再次添加的作用*/ bool judge( char *ID) { int i; for (i = 0; i < num; i++) { if ( strcmp (Students[i].Student_ID, ID) == 0) return false ; } return true ; } /*輸入函數,為了簡化代碼*/ void setInfo( char pinfo[], char desinfo[]) { printf ( "%s:" , pinfo); scanf ( "%s" , desinfo); } /*輸出每個學生的詳細信息*/ void PrintAllInformation() { int i; printf ( "------------------------------------\n" ); for (i = 0; i < num; i++) printf ( "%s %s %s %s %s\n" , Students[i].Student_ID, Students[i].Name, Students[i].Class_Number, Students[i].Attandance_date, Students[i].Attandance_Result); printf ( "------------------------------------\n" ); printf ( "打印成功!按回車鍵返回\n" ); wait_for_Enter(); } /*輸出每個學生的部分信息*/ void PrintPartInformation() { int i; printf ( "------------------------------------\n" ); for (i = 0; i < num; i++) printf ( "%s %s %s\n" , Students[i].Name, Students[i].Attandance_date, Students[i].Attandance_Result); printf ( "------------------------------------\n" ); printf ( "打印成功!按回車鍵返回\n" ); wait_for_Enter(); } /*該函數實現的是向系統中添加同學*/ void ADD() { char ID[maxsize]; printf ( "請輸入學生的學號:" ); scanf ( "%s" , ID); if (!judge(ID)) { printf ( "此人已經存在\n" ); printf ( "添加完成!請按回車鍵返回\n" ); wait_for_Enter(); return ; } strcpy (Students[num].Student_ID, ID); setInfo( "請輸入學生的姓名" , Students[num].Name); setInfo( "請輸入學生的班級" , Students[num].Class_Number); setInfo( "請輸入學生的考勤日期" , Students[num].Attandance_date); setInfo( "請輸入學生的考勤結果出 勤 √ 曠 課 X 事 假 △ 病 假 ○ 遲 到 + 早 退 –" , Students[num].Attandance_Result); num++; //同學人數加一 printf ( "添加成功!按回車鍵返回\n" ); wait_for_Enter(); } /*該函數實現的是查找的功能,可以按學號,姓名,考勤日期進行查找*/ void Find() { int i, op, flag = -1; char information[maxsize]; printf ( "<1>按學號查找 <2>按姓名查找 <3>按考勤日期查找\n" ); scanf ( "%d" , &op); if (op == 1) { printf ( "請輸入該生學號:" ); scanf ( "%s" , information); for (i = 0; i < num; i++) { if ( strcmp (information, Students[i].Student_ID) == 0) printf ( "%s %s %s %s %s\n\n" , Students[i].Student_ID, Students[i].Name, Students[i].Class_Number, Students[i].Attandance_date, Students[i].Attandance_Result), flag = 1; } } else if (op == 2) { printf ( "請輸入該生姓名:" ); scanf ( "%s" , information); for (i = 0; i < num; i++) { if ( strcmp (information, Students[i].Name) == 0) printf ( "%s %s %s %s %s\n\n" , Students[i].Student_ID, Students[i].Name, Students[i].Class_Number, Students[i].Attandance_date, Students[i].Attandance_Result), flag = 1; } } else { printf ( "請輸入考勤日期:" ); scanf ( "%s" , information); for (i = 0; i < num; i++) { if ( strcmp (information, Students[i].Attandance_date) == 0) printf ( "%s %s %s %s %s\n\n" , Students[i].Student_ID, Students[i].Name, Students[i].Class_Number, Students[i].Attandance_date, Students[i].Attandance_Result), flag = 1;; } } if (flag == -1) printf ( "不存在此信息!\n" ); printf ( "查找完成!按回車鍵返回\n" ); wait_for_Enter(); } /*該函數實現的是對系統中已經存在的信息進行修改,類似上面的查找,也可以根據不同的信息選擇想要修改的信息進行修改*/ void Change() { int i, j = -1; char op[2], information[maxsize], find[maxsize]; printf ( "<1>按學號修改 <2>按姓名修改 <3>按考勤日期修改\n" ); scanf ( "%s" , op); if (op[0] == '1' ) setInfo( "請輸入該生學號" , information); else if (op[0] == '2' ) setInfo( "請輸入該生姓名" , information); else setInfo( "請輸入考核日期" , information); strcpy (find, information); if (op[0]== '1' ) for (i = 0; i < num; i++) { if ( strcmp (find, Students[i].Student_ID) == 0) j = i; } else if (op[0]== '2' ) for (i = 0; i < num; i++) { if ( strcmp (find, Students[i].Name) == 0) j = i; } else for (i = 0; i < num; i++) { if ( strcmp (find, Students[i].Attandance_date) == 0) j = i; } if (j == -1) { printf ( "不存在要找的信息!\n" ); printf ( "修改完成!按回車鍵返回\n" ); wait_for_Enter(); return ; } setInfo( "<1>修改學號 <2>修改姓名 <3>修改考勤日期" , op); if (op[0] == '1' ) { setInfo( "請輸入修改后的新學號" , information); strcpy (Students[j].Student_ID, information); } else if (op[0] == '2' ) { setInfo( "請輸入修改后的新姓名" , information); strcpy (Students[j].Name, information); } else { setInfo( "請輸入修改后的新考核日期" , information); strcpy (Students[j].Attandance_date, information); } printf ( "修改完成!按回車鍵返回!\n" ); wait_for_Enter(); } /*該函數實現的是對已經存在于系統種的同學的信息進行刪除操作,具體想法是學生人數num--,后面所有人的信息前移一個位置,實現信息的覆蓋*/ void Delete() { int i, j,flag = -1; char op[2],information[maxsize]; setInfo( "請輸入:<1>按學號刪除<2>按姓名刪除" , op); if (op[0] == '1' ) { setInfo( "請輸入學號" , information); for (i = 0; i < num; i++) { if ( strcmp (Students[i].Student_ID, information) == 0) { flag = 1; for (j = i + 1; j < num; j++) { copy(&Students[i], &Students[j]); } } } } else { setInfo( "請輸入姓名" , information); for (i = 0; i < num; i++) { if ( strcmp (Students[i].Name, information) == 0) { flag = 1; for (j = i + 1; j < num; j++) { copy(&Students[i], &Students[j]); } } } } if (flag == -1) { printf ( "不存在此信息!\n" ); printf ( "刪除完成!按回車鍵返回\n" ); wait_for_Enter(); return ; } num--; printf ( "不存在此信息!\n" ); printf ( "刪除完成!按回車鍵返回\n" ); wait_for_Enter(); } /*此函數實現的是功能5 對系統中已經存在的信息進行輸出*/ void Print() { int op; printf ( "請選擇\n<1>部分打印(輸出學生姓名和考勤日期和考勤情況)\n<2>全部打印(輸出所有學生的所有信息):" ); scanf ( "%d" , &op); if (op == 1) PrintPartInformation(); else PrintAllInformation(); } /*交換兩個變量的所有成員的數值,因為成員都是char數組,所以用到了strcmp函數。此函數主要是為排序進行服務,采用指針進行交換*/ void exchange(student *a, student *b) { char tmp[maxsize]; strcpy (tmp, a->Student_ID); strcpy (a->Student_ID, b->Student_ID); strcpy (b->Student_ID, tmp); strcpy (tmp, a->Name); strcpy (a->Name, b->Name); strcpy (b->Name, tmp); strcpy (tmp, a->Class_Number); strcpy (a->Class_Number, b->Class_Number); strcpy (b->Class_Number, tmp); strcpy (tmp, a->Attandance_Result); strcpy (a->Attandance_Result, b->Attandance_Result); strcpy (b->Attandance_Result, tmp); strcpy (tmp, a->Attandance_date); strcpy (a->Attandance_date, b->Attandance_date); strcpy (b->Attandance_date, tmp); return ; } /*實現功能6排序,可以按學號,姓名,考勤日期進行排序,使用的是冒泡排序對所要求的信息進行排序*/ void Order() { int op, i, j; char tmp[maxsize]; printf ( "請輸入:<1>按學號從小到大排序 <2>按學生姓名ASCLL碼排序 <3>按考勤日期從小到大排序:\n" ); scanf ( "%d" , &op); if (op == 1) { for (i = 0; i < num; i++) { for (j = i+1; j < num; j++) { if ( strcmp (Students[i].Student_ID, Students[j].Student_ID) > 0) { exchange(&Students[i], &Students[j]); } } } } else if (op == 2) { for (i = 0; i < num; i++) { for (j = i+1; j < num; j++) { if ( strcmp (Students[i].Name, Students[j].Name) > 0) { exchange(&Students[i], &Students[j]); } } } } else { int month_1, month_2, day_1, day_2; for (i = 0; i < num; i++) { month_1 = atoi (Students[i].Attandance_date); day_1 = convert(Students[i].Attandance_date); for (j = i+1; j < num; j++) { month_2 = atoi (Students[j].Attandance_date); day_2 = convert(Students[j].Attandance_date); if (month_1>month_2) exchange(&Students[i], &Students[j]); else if (month_1==month_2&&day_1>day_2) exchange(&Students[i], &Students[j]); } } } printf ( "排序成功!按回車鍵返回\n" ); wait_for_Enter(); } /*輸出考勤明細表,考勤明細表具體值得是啥樣的表,是按我個人的理解,如果我理解有問題,可以再叫我改*/ void Detail_Print() { int i, j; char tmp[maxsize]; int month_1, month_2, day_1, day_2; for (i = 0; i < num; i++) { month_1 = atoi (Students[i].Attandance_date); day_1 = convert(Students[i].Attandance_date); for (j = 0; j < num; j++) { month_2 = atoi (Students[j].Attandance_date); day_2 = convert(Students[j].Attandance_date); if (month_1 < month_2) exchange(&Students[i], &Students[j]); else if (month_1 == month_2 && day_1 < day_2) exchange(&Students[i], &Students[j]); } } printf ( "------------------------------------\n" ); printf ( "考勤明細表:\n" ); printf ( "%s:\n" , Students[0].Attandance_date); printf ( "%s %s %s %s\n" , Students[0].Student_ID, Students[0].Name, Students[0].Class_Number, Students[0].Attandance_Result); strcmp (tmp, Students[0].Attandance_date); for (i = 1; i < num; i++) { if ( strcmp (Students[i].Attandance_date, tmp) != 0) { printf ( "%s:\n" , Students[i].Attandance_date); strcmp (tmp, Students[i].Attandance_date); } printf ( "%s %s %s %s\n" , Students[i].Student_ID, Students[i].Name, Students[i].Class_Number, Students[i].Attandance_Result); } printf ( "------------------------------------\n" ); printf ( "打印成功!按回車鍵返回\n" ); wait_for_Enter(); } /*考勤日報表*/ void Daily_Print() { int i; char information[maxsize]; setInfo( "請輸入要查看的日期" , information); printf ( "------------------------------------\n" ); printf ( "%s的考勤情況:\n" , information); for (i = 0; i < num; i++) { if ( strcmp (information, Students[i].Attandance_date) == 0) printf ( "%s %s %s %s\n" , Students[i].Student_ID, Students[i].Name, Students[i].Class_Number, Students[i].Attandance_Result); } printf ( "------------------------------------\n" ); printf ( "打印成功!按回車鍵返回\n" ); wait_for_Enter(); } /*考勤異常表,輸出所有不是 ‘對勾 '的人的信息*/ void Informal_Print() { int i, cnt = 0; printf ( "------------------------------------\n" ); printf ( "考勤異常表:\n" ); for (i = 0; i < num; i++) { if ( strcmp (Students[i].Attandance_Result, "√" ) != 0) printf ( "%s %s %s %s %s\n" , Students[i].Student_ID, Students[i].Name, Students[i].Class_Number, Students[i].Attandance_date, Students[i].Attandance_Result), cnt++; } printf ( "考勤異常人數:%d人\n" , cnt); printf ( "------------------------------------\n" ); printf ( "打印成功!按回車鍵返回\n" ); wait_for_Enter(); } /*請假異常表,輸出所有請假的人的信息*/ void AskForLeave_Print() { int i, cnt = 0; printf ( "------------------------------------\n" ); printf ( "請假異常表\n" ); for (i = 0; i < num; i++) { if ( strcmp (Students[i].Attandance_Result, "○" ) == 0 || strcmp (Students[i].Attandance_Result, "○" ) == 0) printf ( "%s %s %s %s %s\n" , Students[i].Student_ID, Students[i].Name, Students[i].Class_Number, Students[i].Attandance_date, Students[i].Attandance_Result), cnt++; } printf ( "請假人數:%d人\n" , cnt); printf ( "------------------------------------\n" ); printf ( "打印成功!按回車鍵返回\n" ); wait_for_Enter(); } /*事項功能7進行數據統計*/ void Statistics() { char op[2]; setInfo( "請選擇\n<1>考勤明細表\n<2>考勤日報表\n<3>考勤異常表\n<4>請假匯總表" ,op); if (op[0] == '1' ) { Detail_Print(); } else if (op[0] == '2' ) { Daily_Print(); } else if (op[0] == '3' ) { Informal_Print(); } else { AskForLeave_Print(); } } int main() { int i, a, b = 1; FILE *fp= fopen ( "Manage.txt" , "at+" ); if (!fp) { printf ( "錯誤!未能打開文件\n" ); exit (0); } fscanf (fp, "%d" , &num); //讀入已經在系統中的學生的個數 printf ( "當前系統中儲存的學生個數:%d人\n" , num); for (i = 0; i < num; i++) { //讀入系統中學生的信息 fscanf (fp, "%s%s%s%s%s" , &Students[i].Student_ID, &Students[i].Name, &Students[i].Class_Number, &Students[i].Attandance_date, &Students[i].Attandance_Result); } fclose (fp); while (b != 0) { printf ( "==============================================================================\n\n" ); printf ( " 大學生考勤系統\n\n" ); printf ( "==============================================================================\n\n" ); printf ( "<1> 新增學生數據 <2> 查找學生數據 <3>修改學生記錄 \n" ); printf ( "<4>刪除學生記錄 <5> 顯示學生考勤的數據列表 <6> 對指定數據進行排序 \n" ); printf ( "<7>進行數據統計 <8> quit\n\n" ); scanf ( "%d" , &a); switch (a) { case 1: ADD(); break ; case 2: Find(); break ; case 3: Change(); break ; case 4: Delete(); break ; case 5: Print(); break ; case 6: Order(); break ; case 7: Statistics(); break ; case 8: printf ( "已退出\n" ); b = 0; break ; } system ( "cls" ); //清屏函數,為了使界面更加美觀 } //在最后把信息輸入到文件,保留以備下次運行使用 FILE *F= fopen ( "Manage.txt" , "wt" ); //注意這里是wt 只寫打開或建立一個文本文件,只允許寫數據 fprintf (F, "%d\n" , num); for ( int i = 0; i < num; i++) { fprintf (F, "%s %s %s %s %s\n" , Students[i].Student_ID, Students[i].Name, Students[i].Class_Number, Students[i].Attandance_date, Students[i].Attandance_Result); } fclose (F); //文件操作結束 return 0; } |
2020.6.14小小的更新一下:
很多小伙伴私信問我為什么自己機器上運行不了,可能的原因是因為我編寫這個程序時用的是.cpp后綴命名的c++文件,而你用的是.c后綴命名的c語言文件,這二者有著很多的差別,例如c語言里是沒有bool類型的變量的,所以到你們的機器上可能會報錯。
大家可以把.c文件換成.cpp文件再次運行一下,或者bool類型的變量用int類型的0和1來代替,這個程序是絕對沒有問題的。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://blog.csdn.net/weixin_43727229/article/details/102978439