1. 程式人生 > >C小專案——電子詞典

C小專案——電子詞典

C語言專案——查字典

宗旨:技術的學習是有限的,分享的精神是無限的。

【專案需求描述】

一、單詞查詢

給定文字檔案“dict.txt”,該檔案用於儲存詞庫。詞庫為--雙語詞典,每個單詞和其解釋的格式固定,如下所示:

#單詞

Trans:解釋[email protected]解釋[email protected]解釋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:解釋[email protected]解釋[email protected]解釋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);
    }
  }
}

void
DestroyList(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,再分別執行文字查詢,索引建立和索引查詢。