dictionary.c

Download
c 532 lines 16.1 KB
  1/*
  2 * dictionary.c
  3 * ν•΄μ‹œ ν…Œμ΄λΈ”μ„ ν™œμš©ν•œ μ‹€μš©μ μΈ 사전(Dictionary) ν”„λ‘œκ·Έλž¨
  4 *
  5 * κΈ°λŠ₯:
  6 * - 단어 μΆ”κ°€/검색/μ‚­μ œ
  7 * - 전체 λͺ©λ‘ 좜λ ₯
  8 * - 파일 μ €μž₯/뢈러였기
  9 * - 단어 톡계 및 검색 μ œμ•ˆ
 10 * - λŒ€μ†Œλ¬Έμž ꡬ뢄 μ—†λŠ” 검색
 11 */
 12
 13#include <stdio.h>
 14#include <stdlib.h>
 15#include <string.h>
 16#include <ctype.h>
 17#include <stdbool.h>
 18
 19#define TABLE_SIZE 1000
 20#define KEY_SIZE 100
 21#define VALUE_SIZE 500
 22#define FILENAME "dictionary.txt"
 23
 24// λ…Έλ“œ ꡬ쑰체 (체이닝 방식)
 25typedef struct Node {
 26    char word[KEY_SIZE];
 27    char meaning[VALUE_SIZE];
 28    int search_count;       // 검색 횟수
 29    struct Node *next;
 30} Node;
 31
 32// 사전 ꡬ쑰체
 33typedef struct {
 34    Node *buckets[TABLE_SIZE];
 35    int count;
 36    int total_searches;
 37} Dictionary;
 38
 39// 톡계 ꡬ쑰체
 40typedef struct {
 41    char word[KEY_SIZE];
 42    int count;
 43} WordStat;
 44
 45// λŒ€μ†Œλ¬Έμž ꡬ뢄 μ—†λŠ” djb2 ν•΄μ‹œ ν•¨μˆ˜
 46unsigned int hash(const char *key) {
 47    unsigned int hash = 5381;
 48    while (*key) {
 49        hash = ((hash << 5) + hash) + tolower((unsigned char)*key++);
 50    }
 51    return hash % TABLE_SIZE;
 52}
 53
 54// 사전 생성
 55Dictionary* dict_create(void) {
 56    Dictionary *dict = calloc(1, sizeof(Dictionary));
 57    if (!dict) {
 58        fprintf(stderr, "λ©”λͺ¨λ¦¬ ν• λ‹Ή μ‹€νŒ¨\n");
 59    }
 60    return dict;
 61}
 62
 63// 사전 ν•΄μ œ
 64void dict_destroy(Dictionary *dict) {
 65    if (!dict) return;
 66
 67    for (int i = 0; i < TABLE_SIZE; i++) {
 68        Node *current = dict->buckets[i];
 69        while (current) {
 70            Node *next = current->next;
 71            free(current);
 72            current = next;
 73        }
 74    }
 75    free(dict);
 76}
 77
 78// 단어 μΆ”κ°€ λ˜λŠ” μˆ˜μ •
 79void dict_add(Dictionary *dict, const char *word, const char *meaning) {
 80    if (!dict || !word || !meaning) return;
 81
 82    unsigned int index = hash(word);
 83
 84    // κΈ°μ‘΄ 단어 확인
 85    Node *current = dict->buckets[index];
 86    while (current) {
 87        if (strcasecmp(current->word, word) == 0) {
 88            // κΈ°μ‘΄ 단어 μˆ˜μ •
 89            strncpy(current->meaning, meaning, VALUE_SIZE - 1);
 90            current->meaning[VALUE_SIZE - 1] = '\0';
 91            printf("βœ“ '%s' μ—…λ°μ΄νŠΈλ¨\n", word);
 92            return;
 93        }
 94        current = current->next;
 95    }
 96
 97    // μƒˆ 단어 μΆ”κ°€
 98    Node *node = malloc(sizeof(Node));
 99    if (!node) {
100        fprintf(stderr, "λ©”λͺ¨λ¦¬ ν• λ‹Ή μ‹€νŒ¨\n");
101        return;
102    }
103
104    strncpy(node->word, word, KEY_SIZE - 1);
105    node->word[KEY_SIZE - 1] = '\0';
106    strncpy(node->meaning, meaning, VALUE_SIZE - 1);
107    node->meaning[VALUE_SIZE - 1] = '\0';
108    node->search_count = 0;
109
110    node->next = dict->buckets[index];
111    dict->buckets[index] = node;
112    dict->count++;
113
114    printf("βœ“ '%s' 좔가됨\n", word);
115}
116
117// 단어 검색
118char* dict_search(Dictionary *dict, const char *word) {
119    if (!dict || !word) return NULL;
120
121    unsigned int index = hash(word);
122
123    Node *current = dict->buckets[index];
124    while (current) {
125        if (strcasecmp(current->word, word) == 0) {
126            current->search_count++;
127            dict->total_searches++;
128            return current->meaning;
129        }
130        current = current->next;
131    }
132
133    return NULL;
134}
135
136// 단어 μ‚­μ œ
137bool dict_delete(Dictionary *dict, const char *word) {
138    if (!dict || !word) return false;
139
140    unsigned int index = hash(word);
141
142    Node *current = dict->buckets[index];
143    Node *prev = NULL;
144
145    while (current) {
146        if (strcasecmp(current->word, word) == 0) {
147            if (prev) {
148                prev->next = current->next;
149            } else {
150                dict->buckets[index] = current->next;
151            }
152            free(current);
153            dict->count--;
154            printf("βœ“ '%s' μ‚­μ œλ¨\n", word);
155            return true;
156        }
157        prev = current;
158        current = current->next;
159    }
160
161    printf("βœ— '%s'을(λ₯Ό) 찾을 수 μ—†μŠ΅λ‹ˆλ‹€\n", word);
162    return false;
163}
164
165// 전체 단어 λͺ©λ‘ 좜λ ₯
166void dict_list(Dictionary *dict) {
167    if (!dict) return;
168
169    printf("\n╔════════════════════════════════════════════╗\n");
170    printf("β•‘           사전 λͺ©λ‘ (총 %d개)            β•‘\n", dict->count);
171    printf("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•\n\n");
172
173    if (dict->count == 0) {
174        printf("  (λΉ„μ–΄μžˆμŒ)\n");
175        return;
176    }
177
178    int num = 0;
179    for (int i = 0; i < TABLE_SIZE; i++) {
180        Node *current = dict->buckets[i];
181        while (current) {
182            printf("  %3d. %-20s : %s\n",
183                   ++num, current->word, current->meaning);
184            current = current->next;
185        }
186    }
187}
188
189// νŒŒμΌμ— μ €μž₯
190bool dict_save(Dictionary *dict, const char *filename) {
191    if (!dict || !filename) return false;
192
193    FILE *fp = fopen(filename, "w");
194    if (!fp) {
195        fprintf(stderr, "파일 μ—΄κΈ° μ‹€νŒ¨: %s\n", filename);
196        return false;
197    }
198
199    // 헀더 μž‘μ„±
200    fprintf(fp, "# Dictionary File\n");
201    fprintf(fp, "# Count: %d\n\n", dict->count);
202
203    // λͺ¨λ“  단어 μ €μž₯
204    for (int i = 0; i < TABLE_SIZE; i++) {
205        Node *current = dict->buckets[i];
206        while (current) {
207            fprintf(fp, "%s|%s|%d\n",
208                   current->word, current->meaning, current->search_count);
209            current = current->next;
210        }
211    }
212
213    fclose(fp);
214    printf("βœ“ %d개 단어λ₯Ό '%s'에 μ €μž₯ν–ˆμŠ΅λ‹ˆλ‹€\n", dict->count, filename);
215    return true;
216}
217
218// νŒŒμΌμ—μ„œ 뢈러였기
219bool dict_load(Dictionary *dict, const char *filename) {
220    if (!dict || !filename) return false;
221
222    FILE *fp = fopen(filename, "r");
223    if (!fp) {
224        fprintf(stderr, "파일 μ—΄κΈ° μ‹€νŒ¨: %s\n", filename);
225        return false;
226    }
227
228    char line[KEY_SIZE + VALUE_SIZE + 50];
229    int loaded = 0;
230
231    while (fgets(line, sizeof(line), fp)) {
232        // 주석 및 빈 쀄 κ±΄λ„ˆλ›°κΈ°
233        if (line[0] == '#' || line[0] == '\n') continue;
234
235        // μ€„λ°”κΏˆ 제거
236        line[strcspn(line, "\n")] = '\0';
237
238        // νŒŒμ‹±: word|meaning|search_count
239        char word[KEY_SIZE], meaning[VALUE_SIZE];
240        int search_count = 0;
241
242        char *token = strtok(line, "|");
243        if (token) strncpy(word, token, KEY_SIZE - 1);
244
245        token = strtok(NULL, "|");
246        if (token) strncpy(meaning, token, VALUE_SIZE - 1);
247
248        token = strtok(NULL, "|");
249        if (token) search_count = atoi(token);
250
251        // 사전에 μΆ”κ°€ (좜λ ₯ 없이)
252        unsigned int index = hash(word);
253        Node *node = malloc(sizeof(Node));
254        if (!node) continue;
255
256        strncpy(node->word, word, KEY_SIZE - 1);
257        node->word[KEY_SIZE - 1] = '\0';
258        strncpy(node->meaning, meaning, VALUE_SIZE - 1);
259        node->meaning[VALUE_SIZE - 1] = '\0';
260        node->search_count = search_count;
261
262        node->next = dict->buckets[index];
263        dict->buckets[index] = node;
264        dict->count++;
265        loaded++;
266    }
267
268    fclose(fp);
269    printf("βœ“ %d개 단어λ₯Ό '%s'μ—μ„œ λΆˆλŸ¬μ™”μŠ΅λ‹ˆλ‹€\n", loaded, filename);
270    return true;
271}
272
273// 검색 μ œμ•ˆ (λΆ€λΆ„ 일치)
274void dict_suggest(Dictionary *dict, const char *prefix) {
275    if (!dict || !prefix) return;
276
277    printf("\n'%s'둜 μ‹œμž‘ν•˜λŠ” 단어:\n", prefix);
278
279    int found = 0;
280    int len = strlen(prefix);
281
282    for (int i = 0; i < TABLE_SIZE; i++) {
283        Node *current = dict->buckets[i];
284        while (current) {
285            if (strncasecmp(current->word, prefix, len) == 0) {
286                printf("  - %s\n", current->word);
287                found++;
288            }
289            current = current->next;
290        }
291    }
292
293    if (found == 0) {
294        printf("  (μ—†μŒ)\n");
295    } else {
296        printf("총 %d개 발견\n", found);
297    }
298}
299
300// 인기 단어 톡계
301void dict_statistics(Dictionary *dict) {
302    if (!dict) return;
303
304    printf("\n╔════════════════════════════════════════════╗\n");
305    printf("β•‘              사전 톡계 정보                β•‘\n");
306    printf("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•\n\n");
307
308    printf("총 단어 개수:     %d\n", dict->count);
309    printf("총 검색 횟수:     %d\n", dict->total_searches);
310
311    // 검색 횟수 순으둜 μ •λ ¬ (Top 10)
312    WordStat *stats = malloc(sizeof(WordStat) * dict->count);
313    if (!stats) return;
314
315    int idx = 0;
316    for (int i = 0; i < TABLE_SIZE; i++) {
317        Node *current = dict->buckets[i];
318        while (current) {
319            strncpy(stats[idx].word, current->word, KEY_SIZE - 1);
320            stats[idx].count = current->search_count;
321            idx++;
322            current = current->next;
323        }
324    }
325
326    // 버블 μ •λ ¬ (κ°„λ‹¨ν•˜κ²Œ)
327    for (int i = 0; i < dict->count - 1; i++) {
328        for (int j = 0; j < dict->count - i - 1; j++) {
329            if (stats[j].count < stats[j + 1].count) {
330                WordStat temp = stats[j];
331                stats[j] = stats[j + 1];
332                stats[j + 1] = temp;
333            }
334        }
335    }
336
337    // Top 10 좜λ ₯
338    printf("\n인기 단어 Top 10:\n");
339    int limit = dict->count < 10 ? dict->count : 10;
340    for (int i = 0; i < limit; i++) {
341        if (stats[i].count > 0) {
342            printf("  %2d. %-20s (%d회)\n",
343                   i + 1, stats[i].word, stats[i].count);
344        }
345    }
346
347    free(stats);
348}
349
350// 메뉴 좜λ ₯
351void print_menu(void) {
352    printf("\n╔════════════════════════════════════════════╗\n");
353    printf("β•‘           πŸ“– κ°„λ‹¨ν•œ 사전 ν”„λ‘œκ·Έλž¨          β•‘\n");
354    printf("╠════════════════════════════════════════════╣\n");
355    printf("β•‘  1. 단어 μΆ”κ°€                              β•‘\n");
356    printf("β•‘  2. 단어 검색                              β•‘\n");
357    printf("β•‘  3. 단어 μ‚­μ œ                              β•‘\n");
358    printf("β•‘  4. 전체 λͺ©λ‘                              β•‘\n");
359    printf("β•‘  5. 검색 μ œμ•ˆ                              β•‘\n");
360    printf("β•‘  6. 톡계 보기                              β•‘\n");
361    printf("β•‘  7. 파일 μ €μž₯                              β•‘\n");
362    printf("β•‘  8. 파일 뢈러였기                          β•‘\n");
363    printf("β•‘  0. μ’…λ£Œ                                   β•‘\n");
364    printf("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•\n");
365}
366
367// μž…λ ₯ 버퍼 λΉ„μš°κΈ°
368void clear_input(void) {
369    int c;
370    while ((c = getchar()) != '\n' && c != EOF);
371}
372
373// μƒ˜ν”Œ 데이터 λ‘œλ“œ
374void load_sample_data(Dictionary *dict) {
375    dict_add(dict, "apple", "사과; μž₯미과의 낙엽ꡐλͺ©");
376    dict_add(dict, "book", "μ±…; 인쇄물을 μ œλ³Έν•œ 것");
377    dict_add(dict, "computer", "컴퓨터; μ „μž 계산기");
378    dict_add(dict, "dictionary", "사전; 단어λ₯Ό λͺ¨μ•„ μΌμ •ν•œ μˆœμ„œλ‘œ λ°°μ—΄ν•˜μ—¬ μ„€λͺ…ν•œ μ±…");
379    dict_add(dict, "education", "ꡐ윑; 지식과 κΈ°μˆ μ„ κ°€λ₯΄μΉ¨");
380    dict_add(dict, "friend", "친ꡬ; κ°€κΉŒμ΄ 사귀어 μΉœν•˜κ²Œ μ§€λ‚΄λŠ” μ‚¬λžŒ");
381    dict_add(dict, "galaxy", "μ€ν•˜; 우주 곡간에 μžˆλŠ” 천체 집단");
382    dict_add(dict, "happiness", "행볡; 볡된 쒋은 운수");
383    dict_add(dict, "internet", "인터넷; μ „ μ„Έκ³„μ˜ 컴퓨터가 μ„œλ‘œ μ—°κ²°λœ λ„€νŠΈμ›Œν¬");
384    dict_add(dict, "javascript", "μžλ°”μŠ€ν¬λ¦½νŠΈ; μ›Ή ν”„λ‘œκ·Έλž˜λ° μ–Έμ–΄");
385}
386
387// 메인 ν•¨μˆ˜
388int main(void) {
389    Dictionary *dict = dict_create();
390    if (!dict) return 1;
391
392    // μƒ˜ν”Œ 데이터 λ‘œλ“œ
393    printf("μƒ˜ν”Œ 데이터λ₯Ό λΆˆλŸ¬μ˜€λŠ” 쀑...\n");
394    load_sample_data(dict);
395
396    // 기쑴 파일이 있으면 뢈러였기
397    FILE *test = fopen(FILENAME, "r");
398    if (test) {
399        fclose(test);
400        printf("\nκΈ°μ‘΄ 사전 νŒŒμΌμ„ λ°œκ²¬ν–ˆμŠ΅λ‹ˆλ‹€.\n");
401        printf("λΆˆλŸ¬μ˜€μ‹œκ² μŠ΅λ‹ˆκΉŒ? (y/n): ");
402        char choice;
403        scanf(" %c", &choice);
404        clear_input();
405
406        if (choice == 'y' || choice == 'Y') {
407            // κΈ°μ‘΄ 데이터 μ‚­μ œ ν›„ λ‘œλ“œ
408            dict_destroy(dict);
409            dict = dict_create();
410            dict_load(dict, FILENAME);
411        }
412    }
413
414    int choice;
415    char word[KEY_SIZE];
416    char meaning[VALUE_SIZE];
417
418    while (1) {
419        print_menu();
420        printf("선택: ");
421
422        if (scanf("%d", &choice) != 1) {
423            clear_input();
424            printf("βœ— 잘λͺ»λœ μž…λ ₯μž…λ‹ˆλ‹€\n");
425            continue;
426        }
427        clear_input();
428
429        switch (choice) {
430            case 1:  // μΆ”κ°€
431                printf("\n단어: ");
432                fgets(word, KEY_SIZE, stdin);
433                word[strcspn(word, "\n")] = '\0';
434
435                if (strlen(word) == 0) {
436                    printf("βœ— 단어λ₯Ό μž…λ ₯ν•˜μ„Έμš”\n");
437                    break;
438                }
439
440                printf("뜻: ");
441                fgets(meaning, VALUE_SIZE, stdin);
442                meaning[strcspn(meaning, "\n")] = '\0';
443
444                if (strlen(meaning) == 0) {
445                    printf("βœ— λœ»μ„ μž…λ ₯ν•˜μ„Έμš”\n");
446                    break;
447                }
448
449                dict_add(dict, word, meaning);
450                break;
451
452            case 2:  // 검색
453                printf("\n검색할 단어: ");
454                fgets(word, KEY_SIZE, stdin);
455                word[strcspn(word, "\n")] = '\0';
456
457                char *result = dict_search(dict, word);
458                if (result) {
459                    printf("\nβ”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”\n");
460                    printf("β”‚ %s\n", word);
461                    printf("β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€\n");
462                    printf("β”‚ %s\n", result);
463                    printf("β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜\n");
464                } else {
465                    printf("\nβœ— '%s'을(λ₯Ό) 찾을 수 μ—†μŠ΅λ‹ˆλ‹€\n", word);
466                    dict_suggest(dict, word);
467                }
468                break;
469
470            case 3:  // μ‚­μ œ
471                printf("\nμ‚­μ œν•  단어: ");
472                fgets(word, KEY_SIZE, stdin);
473                word[strcspn(word, "\n")] = '\0';
474
475                dict_delete(dict, word);
476                break;
477
478            case 4:  // λͺ©λ‘
479                dict_list(dict);
480                break;
481
482            case 5:  // μ œμ•ˆ
483                printf("\n검색할 접두사: ");
484                fgets(word, KEY_SIZE, stdin);
485                word[strcspn(word, "\n")] = '\0';
486
487                dict_suggest(dict, word);
488                break;
489
490            case 6:  // 톡계
491                dict_statistics(dict);
492                break;
493
494            case 7:  // μ €μž₯
495                dict_save(dict, FILENAME);
496                break;
497
498            case 8:  // 뢈러였기
499                printf("\nν˜„μž¬ 데이터가 μ‚­μ œλ©λ‹ˆλ‹€. κ³„μ†ν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ? (y/n): ");
500                char confirm;
501                scanf(" %c", &confirm);
502                clear_input();
503
504                if (confirm == 'y' || confirm == 'Y') {
505                    dict_destroy(dict);
506                    dict = dict_create();
507                    dict_load(dict, FILENAME);
508                }
509                break;
510
511            case 0:  // μ’…λ£Œ
512                printf("\nμ €μž₯ν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ? (y/n): ");
513                char save_choice;
514                scanf(" %c", &save_choice);
515                clear_input();
516
517                if (save_choice == 'y' || save_choice == 'Y') {
518                    dict_save(dict, FILENAME);
519                }
520
521                printf("사전을 μ’…λ£Œν•©λ‹ˆλ‹€.\n");
522                dict_destroy(dict);
523                return 0;
524
525            default:
526                printf("βœ— 잘λͺ»λœ μ„ νƒμž…λ‹ˆλ‹€\n");
527        }
528    }
529
530    return 0;
531}