Data_Structure/作業/unit4/老師解答/file-records-adds-drops-v2.c
2025-01-20 21:30:53 +08:00

291 lines
9.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Program: file-records-adds-drops-v2.c (Report comments/bugs to chikh@yuntech.edu.tw)
Function: 改良file-records-adds-drops-v1.c使得排序方式可以標準函數庫的qsort()實作
Notes: 1) 本程式實現雙向串列但為了省卻參數傳來傳去衍生的細節困擾宣告head、tail與total(節點
個數)為全域變數,以求扼要
2) 雖head與tail均為全域變數本程式仍刻意設showAll()、sortName()、sortEng()、sortMath()
等函式傳入head引數以利同學發想參數傳遞的基本形式
3) 若把head與tail宣告為main()內的區域變數,同學也應知道如何讓程式正確執行
4) 關於sscanf()的用法,推薦陳鍾誠老師整理的一篇技術短文 http://ccckmit.wikidot.com/cp:sscanf
*/
#include <stdio.h>
#include <stdlib.h> /* for malloc(), calloc(), qsort() */
#include <string.h> /* for strcmp() */
#include <conio.h> /* for getche() */
typedef struct student student;
struct student {
char name[24];
int eng, math;
student *prev;
student *next;
} *head, *tail, dummy;
int total = 0;
void traceBack(student *tail) /* 從頭至尾顯示每一個節點的內容 */
{
student *ptr; /* 指向每一節點所用的指標 */
printf("\n回溯顯示學生紀錄如下:\n");
for (ptr = tail; ptr != NULL; ptr = ptr->prev)
printf("%s\t%d\t%d\n",ptr->name,ptr->eng,ptr->math);
}
void showAll(student *head, student *tail) /* 顯示串列內每一個節點的內容 */
{
student *ptr; /* 指向每一節點所用的指標 */
printf("學生\t\t英文\t數學\n---------------+-------+-----\n");
for (ptr = head; ptr != NULL; ptr = ptr->next)
printf("%s\t%d\t%d\n",ptr->name,ptr->eng,ptr->math);
traceBack(tail);
}
/* 傳入引數為一長字串(譬如Steve Jobs 50 60),從中萃取姓名、英文、數學科成績等
資料形成新節點,並把節點加入串列的尾部(調整tail) */
void addRecord(char line[], int showEntireList)
{
char name[24], str[24];
student *ptr = tail;
//tail = tail->next = (student *)malloc(sizeof(student)); //創建新節點所需空間並成為tail所指的學生紀錄之後的元素
tail = tail->next = (student *)calloc(1,sizeof(student));
tail->prev = ptr;
while (1) {
if (sscanf(line,"%[A-Za-z.] %[^\n]",str,line) == 0) { /* 讀取大小寫字母或句點組成的英文名字讀入str第一個空白之後的所有字串設為line */
sscanf(line,"%d %d",&tail->eng,&tail->math);
break;
}
sprintf(tail->name,tail->name[0]=='\0'? "%s%s": "%s %s",tail->name,str); /* 讀到的英文名加入tail指標所指節點的name欄位之中 */
}
tail->next = NULL;
total++; /* 節點個數增1 */
if (showEntireList) { //從頭顯示串列所有資料
printf("\n增加一筆資料:%s\t%d\t%d\n\n",tail->name,tail->eng,tail->math);
showAll(head,tail);
}
}
/* 傳入引數為一長字串(譬如Steve Jobs 50 60),將從中提取姓名(忽略英文、數學科成績)藉以
比對串列中是否有名字相符的(目標)節點,若有,將把該節點的前後節點串連起來 */
void deleteRecord(char line[])
{
char fullName[24] = {0}, str[24];
student *ptr;
int score;
while (1) {
if (sscanf(line,"%[a-zA-Z.] %[^\n]",str,line) == 0) break;
sprintf(fullName,fullName[0]=='\0'? "%s%s" : "%s %s",fullName,str); //sprintf(fullName,fullName[0]=='\0'? "%s%s " : "%s %s ",fullName,str);
}
for (ptr = head; ptr != NULL; ptr = ptr->next)
if (!strcmp(ptr->name,fullName)) break; /* 找到,立即跳離迴圈 */
if (ptr != NULL) { /* ptr不為空代表發現目標 */
if (ptr->prev==NULL) { /* 若將被刪除的節點是串列頭則調整head為目標下一節點 */
head = ptr->next;
head->prev = NULL;
}
else {
ptr->prev->next = ptr->next; /* 把前一節點與目標下一節點串連起來(繞過目標節點) */
if (ptr != tail)
ptr->next->prev = ptr->prev; /* 下一節點的prev欄位指向前一節點 */
else
tail = ptr->prev;
}
printf("\n移除一筆資料:%s\t%d\t%d\n\n",ptr->name,ptr->eng,ptr->math);
free(ptr); /* 刪除目標節點所佔空間 */
total--; /* 節點個數少1 */
showAll(head,tail);
}
else
printf("擬移除的紀錄(%s)在資料表中不存在\n",fullName);
}
int nameAscend(const void *a, const void *b) /* 遞增模式 */
{
student **x = (student **)a;
student **y = (student **)b;
char lastName[16], nextLastName[16];
sscanf((**x).name,"%s %s %s",lastName,lastName,lastName);
sscanf((**y).name,"%s %s %s",nextLastName,nextLastName,nextLastName);
return strcmp(lastName,nextLastName); //return strcmp((**x).name,(**y).name);
}
int engDescend(const void *a, const void *b) /* 遞減模式 */
{
student **x = (student **)a;
student **y = (student **)b;
return (**y).eng - (**x).eng;
}
int mathDescend(const void *a, const void *b) /* 遞減模式 */
{
student **x = (student **)a;
student **y = (student **)b;
return (**y).math - (**x).math;
}
int sortName(student *head, student *indexName[]) /* 請研讀第二單元之「複習C語言陣列」講義第62-67頁內容 */
{
int i, j;
char lastName[20], nextLastName[20], firstName[20];
student *ptr, *temp;
for (i = 0, ptr = head; ptr != NULL; i++, ptr = ptr->next)
indexName[i] = ptr; /* indexName[i]保存串列第i節點的位址將藉此進行排序 */
qsort(indexName,total,sizeof(indexName[0]),nameAscend);
return 1;
}
/* 傳入引數為串列開頭元素位址與索引陣列indexEng[]的起始位址,將以英文成績高低作為排序依據;
本函式運作原理與上面sortName()相同 */
int sortEng(student *head, student *indexEng[])
{
int i, j;
student *ptr, *temp;
for (i = 0, ptr = head; ptr != NULL; ptr = ptr->next)
indexEng[i++] = ptr; /* indexEng[i]保存串列第i節點的位址將藉此進行排序 */
qsort(indexEng,total,sizeof(indexEng[0]),engDescend);
return 1;
}
/* 傳入引數為串列開頭元素位址與索引陣列indexMath[]的起始位址,本函式將以數學成績高低作為排序依據;
本函式運作原理與上面sortEng()相同 */
int sortMath(student *head, student *indexMath[])
{
int i, j;
student *ptr, *temp;
for (i = 0, ptr = head; ptr != NULL; i++, ptr = ptr->next)
indexMath[i] = ptr; /* indexMath[i]保存串列第i節點的位址將藉此進行排序 */
qsort(indexMath,total,sizeof(indexMath[0]),mathDescend);
return 1;
}
void queryUser(student *head, student *indexName[], student *indexEng[], student *indexMath[])
{
int i, n;
int engSorted = 0, mathSorted = 0; /* 分別紀錄英文科排序與數學科排序是否已執行過 */
while (1) {
printf("\n**************************************************************************************\n" \
"* 1 以姓式排序 2 以英文排序 3 英文第n名紀錄 4 以數學排序 5 數學第n名紀錄 0 結束 *\n" \
"**************************************************************************************\n" \
"選擇將執行哪一項功\能?(0--5) ==> ");
switch (getche()) {
case '1':
printf("\n\n以姓式排序結果如下:\n");
sortName(head,indexName);
for (i = 0; i < total; i++)
printf("%s\t%d\t%d\n",indexName[i]->name,indexName[i]->eng,indexName[i]->math);
break;
case '2':
printf("\n\n以英文成績排序結果如下:\n");
engSorted = sortEng(head,indexEng);
for (i = 0; i < total; i++)
printf("%s\t%d\t%d\n",indexEng[i]->name,indexEng[i]->eng,indexEng[i]->math);
break;
case '3':
if (!engSorted)
printf("\n尚未以英文科成績高低作排序,請先選擇\'2\'\\n");
else {
printf("\n查詢英文第n名的資料輸入n (1-%d) ==> ",total);
scanf("%d",&n);
printf("英文第%d高分者%s\t%d\t%d\n",n,indexEng[n-1]->name,indexEng[n-1]->eng,indexEng[n-1]->math);
}
break;
case '4':
printf("\n\n以數學成績排序結果如下:\n");
mathSorted = sortMath(head,indexMath);
for (i = 0; i < total; i++)
printf("%s\t%d\t%d\n",indexMath[i]->name,indexMath[i]->eng,indexMath[i]->math);
break;
case '5':
if (!mathSorted)
printf("\n尚未以數學科成績高低作排序,請先選擇\'4\'\\n");
else {
printf("\n查詢數學第n名的資料輸入n (1-%d) ==> ",total);
scanf("%d",&n);
printf("數學第%d高分者%s\t%d\t%d\n",n,indexMath[n-1]->name,indexMath[n-1]->eng,indexMath[n-1]->math);
}
break;
case '0':
return;
default:
printf("有效的選擇為0--5請重新輸入\n");
system("pause");
}
}
}
int main()
{
int i;
char line[120], mode; /* mode用於區分增減資料的工作模式 */
tail = &dummy; /* dummy為暫時用途的節點導入此節點將使得tail或head初始運作避免出錯同學可理解這樣寫與你原先的做法有何差別? */
FILE *inputFile;/* 輸入檔案在程式內部的代稱 */
if ((inputFile=fopen("YunTechStudents.txt","r")) == NULL) { /* 設定開檔模式為輸入用途檢視檔案可否開啟若input為空代表檔案不存在 */
printf("輸入檔不存在\n");
exit(1); /* 退出程式 */
}
for (i = 0; i < 4; i++) fgets(line,120,inputFile); /* 輸入文檔開頭的四列文字消化掉 */
while (fgets(line,120,inputFile) != NULL) { /* 未觸及檔案末端,代表尚有資料可讀取,重複迴圈內動作 */
//讀取一整列內容存入line字串變數之中
if (!strcmp(line,"**************************************\n")) break; /* 已讀取到分隔線,將切換到另一處理模式 */
addRecord(line,0); //讀取到的整列內容(譬如Steve Jobs 50 60)交由addRecord()萃取入串列false代表不需從頭顯示串列所有資料
}
printf("第一階段資料讀取完畢,共有%d筆記錄\n\n",total);
head = dummy.next; /* 定調head指向dummy.next所指節點 */
head->prev = NULL;
showAll(head,tail);
printf("\n以上為輸入檔案\"分隔線\"以上的資料,按任意鍵開始增減資料 ...\n"); //system("pause");
getche();
while (fgets(line,120,inputFile) != NULL) {
sscanf(line,"%c %[^\n]s",&mode,line); /* 讀取'+'或'-'設入mode變數中界定不同的處理模式 */
if (mode == '+') /* 增加紀錄 */
addRecord(line,1); /* 整列line變數交由addRecord()萃取所需資料,並從頭顯示串列所有資料 */
else if (mode == '-') /* 刪減紀錄 */
deleteRecord(line); /* line所含姓名將由deleteRecord()比對,若存在於串列,將移除該筆紀錄並從頭顯示串列所有資料 */
else
printf("未知指令sorry :(\n");
}
fclose(inputFile);
student *indexName[100], *indexEng[100], *indexMath[100]; /* 欄位的索引陣列預設可記100個節點位址 */
/* 若不想使用如上固定長度的陣列可改依total數值分配相應元素個數的陣列但請留意底下寫法 */
/*
student **indexName = (student **)malloc(total*sizeof(student *)),
**indexEng = (student **)malloc(total*sizeof(student *)),
**indexMath = (student **)malloc(total*sizeof(student *));
*/
queryUser(head,indexName,indexEng,indexMath); /* 產生功能選單,並依選項執行相應的功能 */
printf("程式結束Bye\n");
return 0;
}