C小项目——电子词典

时间:2023-01-20 20:48:28

C语言项目——查字典


宗旨:技术的学习是有限的,分享的精神是无限的。


【项目需求描述】

一、单词查询

给定文本文件“dict.txt”,该文件用于存储词库。词库为--双语词典,每个单词和其解释的格式固定,如下所示:

#单词

Trans:解释1@解释2@…解释n

每个新单词由“#”开头,解释之间使用“@”隔开。一个词可能有多个解释,解释均存储在一行里,行首固定以“Trans开头。下面是一个典型的例子:

#abyssinian

Trans:a. 阿比西尼亚的@n. 阿比西尼亚人;依索比亚人

该词有两个解释,一个是“a. 阿比西尼亚的;另一个是“n. 阿比西尼亚人;依索比亚人

要求编写程序将词库文件读取到内存中,接受用户输入的单词,在字典中查找单词,并且将解释输出到屏幕上。用户可以反复输入,直到用户输入“exit”字典程序退出。

程序执行格式如下所示:

./app –text

-text表示使用文本词库进行单词查找。

二、建立索引,并且使用索引进行单词查询

要求建立二进制索引,索引格式如下图所示。将文本文件“dict.txt”文件转换为上图所示索引文件“dict.dat”,使用索引文件实现单词查找。程序执行格式如下:

./app –index

-index表示使用文本词库dict.txt建立二进制索引词库dict.dat

./app –bin

-bin表示使用二进制索引词库进行单词查找。

//================================================================================================================

一、文本文件单词查询

1、单词结构:

#单词

Trans:解释1@解释2@…解释n

dict.txt文件中,单词占一行,以“#”开头;解释以Trans:开头,内容以“@”分隔。结构我采用链表。具体结构定义如下:

// 单词链表 ---- 单词名称,翻译的个数,单词翻译的结果

typedef struct dict

{

  char word[TARGET_WORD_MAX_SIZE]; // 要输入的单词,如"#superstar"

  uint8_t mean_count;  // 单词解释的个数,如既可以做名词也可以做动词,

  char trans[TARGET_WORD_MEANING_COUNT][TARGET_WORD_MAX_TRANSLATION]; // 翻译结果,用@分开strtok分割函数)

  struct dict *next;

} word_t, *dict_t;

2、接口定义

2.1、文件中单词的总数:要建立单链表,就要知道单链表的长度,因此,要知道文件中单词的总个数,定义如下接口:

uint32_t ListCount(FILE *fp); // 词典里面单词的个数,即是要创建链表的长度

2.2、创建单链表:从文件中一项一项读出单词及其解释,填充到单词结构体中,创建单链表就是分配内存并连接节点的过程,定义接口如下(count是单词的总数):

dict_t CreateList(dict_t head, FILE *fp, uint32_t count); // 创建单链表,返回首节点。分配内存。

2.3、查找单词:从链表中匹配要查找的单词,找到了就输出单词解释,定义接口如下:

void SearchList(dict_t head, uint32_t count); // 查找输入的单词

2.4、释放内存:创建链表分配的内存在结束时释放,定义接口如下:

void DestroyList(dict_t head); // 释放内存


二、建立索引文件dict.data

1、索引结构

如上图所示,包含如下内容:索引头单词个数(4字节),单词1的单词长度(4字节),单词1的内容(单词长度个字节),单词1的解释个数(4字节),解释1的长度(4字节),解释1的内容(解释1的长度),解释2的长度(4字节),解释2的内容(解释2的长度)...按照这个格式将dict.txt文件的内容写到dict.dat的索引文件中。结构和上面文本查询的结构一样。

2、接口定义

  将链表的节点按上面的格式一个一个写到索引文件dict.dat中,用fwrite函数写,定义接口如下:

// 链表头结点head,文件名,链表长度

void WriteIndexFile(dict_t head, const char *filename, uint32_t count);


三、索引文件查找单词

上面建立了索引文件,并按协议的格式将文本文件的内容写到了索引文件中,通过索引查找单词就是从索引文件读出要查找的单词,用fread函数读,接口定义如下:

void ReadIndexFile(dict_t head, const char *filename, uint32_t *count);


具体实现:

#ifndef _TARGET_H_
#define _TARGET_H_

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

typedef unsigned char uint8_t;
typedef unsigned int uint32_t;

#define TARGET_TEXT_NAME "./dict.txt"
#define TARGET_INDEX_NAME "./dict.dat"
#define TARGET_WORD_MAX_SIZE 60
#define TARGET_WORD_MEANING_COUNT 20
#define TARGET_WORD_MAX_TRANSLATION 100
#define TARGET_WORD_BUFFER 1024

// 单词链表 ---- 单词名称,单词有几种翻译,单词翻译的结果
typedef struct dict
{
char word[TARGET_WORD_MAX_SIZE]; // 要输入的单词,如"#superstar"
uint8_t mean_count; // 单词解释的个数,如既可以做名词也可以做动词,用@分开
char trans[TARGET_WORD_MEANING_COUNT][TARGET_WORD_MAX_TRANSLATION]; // 翻译结果
struct dict *next;
} word_t, *dict_t;


uint32_t ListCount(FILE *fp); // 词典里面单词的个数,即是要创建链表的长度
dict_t CreateList(dict_t head, FILE *fp, uint32_t count); // 创建单链表,返回首节点。分配内存。
void SearchList(dict_t head, uint32_t count); // 查找输入的单词
void DestroyList(dict_t head); // 释放内存

void WriteIndexFile(dict_t head, const char *filename, uint32_t count);
void ReadIndexFile(dict_t head, const char *filename, uint32_t *count);

void Process(int argc, char **argv); // 主进程,main函数主要调用接口

#endif /* _TARGET_H_ */

main.c

#include "target.h"

int
main(int argc, char **argv)
{
if(argc < 2)
{
fprintf(stderr, "input params is too few!\n");

return 1;
}

Process(argc, argv);

return 0;
}

process.c:

#include "target.h"

static const char params[][15] = {"-text", "-index", "-bin", "-test1 -f", "-test2 -f"};

void
Process(int argc, char **argv)
{
FILE *fp;
dict_t head;
uint32_t count;

if((fp = fopen(TARGET_TEXT_NAME, "r")) == NULL)
{
fprintf(stderr, "open file failure!\n");
exit(1);
}
count = ListCount(fp);

printf("count: %d\n", count);
printf("open sucess!\n");

if((strcmp(argv[1], params[0])) == 0)
{
head = CreateList(head, fp, count);
SearchList(head, count);
fclose(fp);
DestroyList(head);
}

if((strcmp(argv[1], params[1])) == 0)
{
head = CreateList(head, fp, count);
fclose(fp);
WriteIndexFile(head, TARGET_INDEX_NAME, count);
DestroyList(head);
}

if(strcmp(argv[1], params[2]) == 0)
{
head = CreateList(head, fp, count);
ReadIndexFile(head, TARGET_INDEX_NAME, &count);
SearchList(head, count);
fclose(fp);
DestroyList(head);
}
}

find_word_from_text.c

#include "target.h"

static char file_exist;

uint32_t
ListCount(FILE *fp) // 单词的个数即是链表的长度
{
uint32_t count = 0;
char buffer[100];

while(fgets(buffer, sizeof(buffer), fp))
{
if('#' == buffer[0])
{
++count;
}
}
rewind(fp); // 这一步一定要做,使文件指针指向文件头

return count;
}


dict_t
CreateList(dict_t head, FILE *fp, uint32_t count) // 创建链表,返回头结点
{
dict_t new, pointer;
char buf[TARGET_WORD_BUFFER];
uint8_t word_size, trans_size, mean_count = 1, *str;
uint32_t i, j = 0;

head = (dict_t)malloc(sizeof(word_t)); //分配节点空间
if(NULL == head)
{
fprintf(stderr, "malloc failure!\n");
exit(1);
}
printf("head success!\n");

if(count > 0)
{
memset(buf, 0, sizeof(buf));
fgets(buf, sizeof(buf), fp);
word_size = strlen(buf);
buf[word_size - 1] = '\0';
strcpy(head->word, buf);

memset(buf, 0, sizeof(buf));
fgets(buf, sizeof(buf), fp);
trans_size = strlen(buf);
buf[trans_size - 1] = '\0';

str = strtok(buf, "@");
strcpy(head->trans[j++], str + 6);

while(str = strtok(NULL, "@"))
{
strcpy(head->trans[j++], str);
++mean_count;
}
head->mean_count = mean_count;

head->next = NULL; // 到这里为止填充了首节点,并将首节点的下一个节点指向空
pointer = head;

for(i = 0; i < count - 1; ++i) // 将后面(count-1)个依次链接到首节点后面
{
mean_count = 1;
new = (dict_t)malloc(sizeof(word_t)); //分配节点空间

memset(buf, 0, sizeof(buf));
fgets(buf, sizeof(buf), fp);
word_size = strlen(buf);
buf[word_size - 1] = '\0';
strcpy(new->word, buf);

memset(buf, 0, sizeof(buf));
fgets(buf, sizeof(buf), fp);
trans_size = strlen(buf);
buf[trans_size - 1] = '\0';
for(j = 0; j < count;)
{
str = strtok(buf, "@");
strcpy(new->trans[j++], str + 6);

while(str = strtok(NULL, "@"))
{
strcpy(new->trans[j++], str);
++mean_count;
}
}
new->mean_count = mean_count;
new->next = NULL;

pointer->next = new;
pointer = new;
}
}
rewind(fp);

return head;
}

void
PrintList(dict_t head)
{
dict_t pointer;
pointer = head;

while(pointer != NULL)
{
printf("pointer->word = %s, pointer->mean_count = %d\n", pointer->word, pointer->mean_count);
pointer = pointer->next;
}
}

void
SearchList(dict_t head, uint32_t count) // 从链表中查找单词
{
dict_t pointer;
char str[TARGET_WORD_MAX_SIZE];
uint32_t i;

while(1)
{
file_exist = 0;
pointer = head;
printf("Please input a word:");
fgets(str, TARGET_WORD_MAX_SIZE, stdin);
str[strlen(str) - 1] = '\0';

if(strcmp(str, "exit") == 0)
{
exit(1);
}

while(pointer != NULL)
{
if((strcmp(pointer->word, str)) == 0)
{
for(i = 0; i < pointer->mean_count; ++i)
{
file_exist = 1;
fprintf(stdout, "Trans%d: %s\n", i + 1, pointer->trans[i]);
}
break;
}

pointer = pointer->next;
}
if(file_exist == 0)
{
    // 这里判断了该单词不存在,可以选择添加,也可以选择退出;/*      printf("no find!\n");      printf("Do you want to add a new word?:(Y/N)\n");      scanf("%c", &new_word);      if(new_word == 'Y' || new_word == 'y')      {        AddWordToText(head, TARGET_CUSTOM_TEXT);      }*/      exit(1);    }  }}voidDestroyList(dict_t head) {  dict_t pointer;  while(pointer != NULL)  {    pointer = head;    head = head->next;    free(pointer);  }}

find_word_from_index.c

#include "target.h"

void
WriteIndexFile(dict_t head, const char *filename, uint32_t count) // 建立索引,按协议格式写入文件
{
FILE *stream;
uint32_t i, word_size, trans_size, mean_count;
uint8_t j;
dict_t pointer = head;

if((stream = fopen(filename, "wb")) == NULL)
{
fprintf(stderr, "Cannot open output file.\n");
exit(1);
}

fwrite(&count, 4, 1, stream);

while(pointer != NULL)
{
word_size = strlen(pointer->word);
mean_count = pointer->mean_count;

fwrite(&word_size, 4, 1, stream);
fwrite(pointer->word, 1, word_size, stream);
fwrite(&mean_count, 4, 1, stream);

for(j = 0; j < mean_count; ++j)
{
trans_size = strlen(pointer->trans[j]);
fwrite(&trans_size, 4, 1, stream);
fwrite(pointer->trans[j], 1, trans_size, stream);
}

pointer = pointer->next;
}

fclose(stream);
}

void
ReadIndexFile(dict_t head, const char *filename, uint32_t *count) // 从文件中读出单词内容
{
FILE *stream;
uint8_t i;
uint32_t word_size, trans_size, mean_count;
dict_t pointer = head;

printf("enter...\n");

if((stream = fopen(filename, "rb")) == NULL)
{
fprintf(stderr, "Cannot open output file.\n");
exit(1);
}
// printf("read file success!...\n");
fread(count, 4, 1, stream);
printf("count = %d\n", *count);
while(pointer != NULL)
{
fread(&word_size, 4, 1, stream);
// printf("word_size = %d\n", word_size);

fread(pointer->word, 1, word_size, stream);
pointer->word[word_size] = '\0';
fread(&pointer->mean_count, 4, 1, stream);

//printf("pointer->word = %s\n", pointer->word);
//printf("pointer->mean_count = %d\n", pointer->mean_count);
for(i = 0; i < pointer->mean_count; ++i)
{
memset(pointer->trans[i], 0, sizeof(pointer->trans[i]));
fread(&trans_size, 4, 1, stream);
fread(pointer->trans[i], 1, trans_size, stream);
pointer->trans[i][trans_size] = '\0';
printf("trans_size = %d\n", trans_size);
printf("pointer->trans = %s\n", pointer->trans[i]);
}
pointer = pointer->next;
}
//fclose(stream);

printf("read over!\n");
}

先用gcc编译生成可执行文件app,再分别执行文本查询,索引建立和索引查询。