下面以一个电子英汉词典程序(以下简称电子词典)为例,应用双向循环链表。分离数据结构,可以使逻辑代码独立于数据结构操作代码,程序结构更清晰,代码更简洁;电子词典的增、删、查、改操作分别对应于链表的插入、删除、查找、查找和获取链表元素操作。
在程序初始化时,除了初始化链表,还要将保存在文件中的词库加载到链表中:
1 void dict_init() { 2 list = linkedlist_new(); 3 4 dict_load(); 5 printf("Welcome."); 6 }
函数dict_load()实现如下:
1 static void dict_load() { 2 FILE * fp; 3 struct Word word; 4 5 while (!(fp = fopen(PATH, "rb"))) { 6 fp = fopen(PATH, "wb"); 7 fclose(fp); 8 } 9 assert(fp); 10 11 fread(&word, sizeof(struct Word), 1, fp); 12 while (!feof(fp)) { 13 linkedlist_insert(list, TRAVELDIR_BACKWARD, 1, word); 14 fread(&word, sizeof(struct Word), 1, fp); 15 } 16 17 fclose(fp); 18 }
函数feof()应先读后判断,所以在进入循环之前应先读一次。
当然,在程序结束前,也要将链表中的词组保存到文件中,函数dict_store()实现如下:
1 static void dict_store() { 2 FILE * fp; 3 const int count = linkedlist_length(list); 4 5 assert(fp = fopen(PATH, "wb")); 6 for (int i = 0; i < count; i++) { 7 fwrite(linkedlist_get(list, TRAVELDIR_FORWARD, i + 1), 8 sizeof(struct Word), 1, fp); 9 } 10 11 fclose(fp); 12 }
一个英汉词组包含一个英文,一个中文,可以把它定义为一个结构体,并让它作为链表节点数据域的数据类型;修改linkedlist.h中LinkedlistData的相关定义:
1 typedef struct Word { 2 string eng; 3 string chn; 4 } LinkedListData;
C语言中没有string类型,可以使用定长的字符数组来定义它;再定义一个函数mygets()用来代替scanf()函数,以确保输入的内容不会超过定义的字符串长度:
1 #define MAX_STR_LEN 8 2 typedef char string[MAX_STR_LEN]; 3 4 void mygets(char * s) 5 { 6 __fpurge(stdin); 7 fgets(s, MAX_STR_LEN, stdin); 8 while (*s++) { 9 *s = *s == ' ' ? 0 : *s; 10 } 11 }
在Linux下,使用__fpurge()函数来代替Windows下的fflush()函数清空输入流。
接下来就是电子词典的增、删、查、改操作:
1 void dict_add(const char * eng) { 2 struct Word word; 3 strcpy(word.eng, eng); 4 5 printf("The word does not exist, add it? y/n>"); 6 if (__fpurge(stdin), getchar() == 'y') { 7 printf("Ok, what does it mean? >"); 8 mygets(word.chn); 9 10 linkedlist_insert(list, TRAVELDIR_BACKWARD, 1, word); 11 printf("The word is existed now. "); 12 } 13 } 14 15 void dict_delete() { 16 int location; 17 struct Word word; 18 19 printf("What word do you wanna delete? >"); 20 mygets(word.eng); 21 22 if ((location = linkedlist_locate(list, TRAVELDIR_FORWARD, word, dict_cmp)) 23 != -1) { // found 24 struct Word * pWord = linkedlist_get(list, TRAVELDIR_FORWARD, location); 25 26 printf("Delete: %s %s Are you sure? y/n>", pWord->eng, pWord->chn); 27 if (__fpurge(stdin), getchar() == 'y') { 28 linkedlist_delete(list, TRAVELDIR_FORWARD, location); 29 printf("The word is deleted now. "); 30 } 31 } else { // not found 32 printf("The word does not exist. "); 33 } 34 } 35 36 void dict_search(const char * eng) { 37 int location; 38 struct Word word; 39 strcpy(word.eng, eng); 40 41 if ((location = linkedlist_locate(list, TRAVELDIR_FORWARD, word, dict_cmp)) 42 == -1) { // not found 43 dict_add(eng); 44 } else { // found 45 printf("%s ", linkedlist_get(list, TRAVELDIR_FORWARD, location)->chn); 46 } 47 } 48 49 void dict_modify() { 50 int location; 51 struct Word word; 52 53 printf("What word do you wanna modify? >"); 54 mygets(word.eng); 55 56 if ((location = linkedlist_locate(list, TRAVELDIR_FORWARD, word, dict_cmp)) 57 != -1) { // found 58 struct Word * pWord = linkedlist_get(list, TRAVELDIR_FORWARD, location); 59 60 printf("Ok, what does it mean? >"); 61 mygets(pWord->chn); 62 printf("The word is modified now. "); 63 } else { // not found 64 printf("The word does not exist. "); 65 } 66 }
dict_cmp()函数作为参数传递给linkedlist_locate()函数,用以比较词组是否相同,相同则返回0,它的实现如下:
1 int dict_cmp(const void * s1, const void * s2) { 2 return strcmp(((LinkedListData *) s1)->eng, ((LinkedListData *) s2)->eng); 3 }
还需要一个函数来组织这些子函数的调用:
1 void dict_show() { 2 while (1) { 3 string str; 4 printf(" >"); 5 mygets(str); 6 7 if (!strcmp(str, "quit;")) { 8 dict_store(); 9 linkedlist_destory(&list); 10 printf("Bye. "); 11 return; 12 } else if (!strcmp(str, "delete;")) { 13 dict_delete(); 14 } else if (!strcmp(str, "modify;")) { 15 dict_modify(); 16 } else { 17 dict_search(str); 18 } 19 } 20 }
最后,编写主函数,电子词典就大功告成了!