티스토리 뷰

 안녕하세요. 지난 시간에 이어 검색어 자동완성 기능 구현에 대해 알아보도록 하겠습니다.

 지난 시간에는 한글을 자동완성 기능 구현에 적합한 형태의 자소 단위로 변환하는 방법에 대해 알아보았습니다. 이번 시간에서는, 지난번에 만들었던 모듈을 이용해, 사용자가 검색어를 입력하면, 입력한 검색어로 시작되는 추천 검색어들을 출력해 주는 프로그램을 만들어 보도록 하겠습니다. 여기까지 구현된다면, 남은 부분은 AJAX를 이용해 웹브라우져로 보여주는 것 밖에 없습니다.

 그럼 자세한 부분을 설명하기 앞서, 먼저 오늘 만들어 볼 프로그램의 개략적인 흐름에 대해서 알아보도록 하겠습니다.

 오늘은 두 개의 프로그램을 만들어 볼 것입니다. 검색어 자동완성 기능에 필요한 데이터를 구축하는 프로그램, 그리고 구축된 데이터를 이용해 추천 검색어를 검색하는 프로그램입니다. 그럼 먼저, 데이터를 구축하는 프로그램에 대해서 알아보도록 하겠습니다.

 검색어 자동완성 기능에 필요한 데이터를 구축하는 프로그램에는 먼저  추천 검색어들을 지정해 주어야 하겠죠? 추천 검색어들은 어떻게 지정할까요? 가장 간단히 생각하면, 관리자가 추천 검색어 목록을 넘겨주는 것이겠죠. 하지만 관리자가 수많은 추천 검색어들을 관리할 수 없다면, 사용자가 입력한 검색어(검색 버튼을 클릭해 검색을 한)를 검색된 빈도수와 함께 DB에 저장한 후, 빈도수가 높은 순서대로 추천 검색어로 출력이 되도록 하는 방법이 있을 것입니다. 그래서 우리는 다음과 같은 텍스트 파일을 이용해 추천 검색어를 지정하도록 할 것입니다.

 

word_list.txt

 

괴물 300

국민은행 40

기상청 50

곰플레이어 다운로드 10

각설탕 20

도현 밴드 24

이경민 100

구글 23

김옥빈 사진 40

다음 40

다나와 20

해수욕장중 가장 괜찮은 곳 30

해신 15

 

각 줄에서, 첫 번째 나오는 단어는 추천 검색어에 포함될 수 있는 후보 단어들입니다. 그리고 두 번째 나오는 숫자는 첫 번째 나오는 단어의 빈도수 입니다. 예를 들어, ㄱ을 입력하면, 괴물, 국민은행, 기상청 등과 같이 ㄱ으로 시작되는 검색어들이 추천되겠지만 보여지는 순서는 두 번째 나오는 숫자가 높은 순서대로 보여지게 됩니다. 또 보여지는 추천 검색어 개수를 제한하고 싶을 때, 빈도수가 높은 순대로 정렬되어 상위 N개만 보여지도록 될 것입니다.

 word_list.txt 파일에 추천 검색어 목록을 지정한 후, 프로그램을 실행시키면, 이 파일에서 추천 검색어와 숫자를 읽어서 추천 검색어를 자소 단위로 분리하여 숫자와 함께 자소 단위로 정렬하여 stl vector에 입력합니다. 모든 추천 검색어를 map에 입력한 후, map에서 자소 단위로 분리된 추천 검색어와 숫자를 하나씩 읽어온 후, 읽어온 추천 검색어의 자소를 하나씩 읽으면서 읽어온 자소는 B-TREE의 키 부분에, 추천 검색어는(자소 단위로 분리되지 않은) 데이터 부분에 넣습니다. 이런 과정을 모든 추천 검색어에 대해 반복하면, 검색어 자동완성 기능에 필요한 모든 데이터를 구축하게 됩니다.

 여기에서 map B-TREE라는 두 개의 자료구조가 나오네요. 두 개의 자료구조에 대해 처음 접하시는 분들도 있을 테니, 간단히 설명하도록 하고 넘어가도록 하겠습니다. 먼저 map, 설명하기 앞서 stl에 대해 설명해야 겠네요.

 stl(standard template library)은 우리들이 흔히 필요할 수 있는 string, linked list, vector, map, set 등과 같은 자료구조를 편히 쓸 수 있도록 구현해 놓은 표준 라이브러리를 말합니다. 그 중에서 우리가 쓸 vector는 배열(array)과 비슷한 개념이지만, 일반 배열에 비해서 더 많은 정보를 제공해 준다는 점, vector에 크기는 동적으로 변할 수 있다는 점에서 배열보다 더 편리하게 사용할 수 있습니다.

 B-TREE는 데이터를 정렬해서 관리하는데 그 양이 방대해서 메모리에 모두 올려놓고 관리할 수 없을 때, 하드 디스크를 액세스하는 횟수를 최대한 줄이며, 데이터를 정렬 상태를 유지하면서 삽입과 삭제를 할 수 있도록 구현된 자료구조입니다. 우리가 사용할 B-TREE의 노드는 키 부분과 데이터를 가리키는 포인터로 이루어져 있습니다. 키는 문자열이 들어가고 데이터를 가리키는 포인터에는 int형 숫자가 들어갑니다.

 이상으로 간단히 vector B-TREE에 간단히 대해 알아보았습니다. 그럼 이제 프로그램의 소스를 살펴보면서 진행하도록 하겠습니다.

 먼저 word_list.txt에 파일에서 추천 검색어와 빈도수를 읽어서 자소 단위로 분리하여 vector에 넣는 코드를 살펴 보도록 하겠습니다.

 

typedef struct{

             int org_str;

             int dsbl_str;

             int freq;

}DSBL_WORD;

 

 위 구조체는 하나의 추천 검색어에 대한 정보를 나타내기 위해 쓰입니다.

멤버 변수 중 org_str은 원본 문자열을 나타내고. dsbl_str은 자소 단위로 분리된 문자열을, freq는 빈도수를 나타냅니다. 그런데 문자열을 나타내는 변수가 좀 특이하네요? 문자열은 char형 배열이나 포인터를 이용해 나타내야 하는데 int형 변수를 사용하고 있습니다. 이것은 org_str이나 dsbl_str 변수가 문자열을 직접적으로 가리키고 있는 것이 아니라, 실제 문자열은 커다란 버퍼에 저장해 놓고, 저장되어 있는 위치에서부터 버퍼가 시작되는 곳까지의 거리(offset) 저장하고 있는 것입니다. 이렇게 구현하는 이유는, 메모리를 절약하기 위해서입니다. , 문자열의 길이가 추천 검색어마다 다를 수 있으므로, 모든 검색어를 포함할 수 있도록 충분히 크게 char org_str[50]과 같이 배열로 선언을 하면, 많은 공간이 낭비될 것입니다. 그리고, 검색어의 길이를 특정 길이로 제한해야 하죠. 또 배열에 저장되어 있는 주소를 직접 가리키지 않고 offset 형태로 저장하는 이유는, 나중에 파일로 저장되어 프로그램이 종료되고 다시 실행되어 파일에서 문자열을 버퍼로 읽어왔을 때에도 값이 유효하도록 하기 위해서 입니다.

 커다란 버퍼에 실제 문자열을 입력한다고 하였는데, 이런 버퍼의 관리를 편하게 하기 위해 CBufQue라는 클래스를 만들어서 사용합니다. CBufQue 클래스의 사용법은 매우 간단한데, 생성자의 인자로는 처음 확보되는 버퍼의 크기를 지정합니다. 그리고 enqueue라는 함수를 통해 문자열을 버퍼에 추가합니다. enqueue 함수는 문자열이 입력된 위치를 offset 형태로 반환하기 때문에, 이 값을 받아서 저장하면 됩니다. 실제 문자열을 나타낼 때는 toStr이라는 함수에 enqueue에서 반환한 값을 넘겨주면 됩니다. 남아있는 버퍼의 크기보다 입력된 문자열의 크기가 더 클 경우에는 자동으로 버퍼의 크기를 늘려줍니다. 그리고 write read 함수를 이용해 버퍼의 내용을 파일에 쓰거나, 파일에서 읽어옵니다.

 아래 코드를 보면 g_wbuf g_dwbuf 두 개의 CBufQue 인스턴스가 선언되고 있습니다. g_wbuf에는 추천 검색어의 원래 문자열이 저장되고, g_dwbuf에는 자소 단위로 분리된 문자열이 저장이 됩니다.

 

vector g_wlist;

const int MAX_SGG_WORD_LEN = 1000;

CBufQue g_wbuf(1024);

CBufQue g_dwbuf(1024);

 

int read_word(FILE* word_list_fp,char* word,int* freq)

{

             if(fgets(word,MAX_SGG_WORD_LEN,word_list_fp) == NULL)

                           return FALSE;

 

             int len = strlen(word);

             char freq_buf[16];

             int i = 0;

             while(--len)

             {

                           char w = word[len];

                           if(w >= '0' && w <= '9')

                                        freq_buf[i++] = w;

                           else if(i > 0)

                                        break;

             }

             *freq = 0;

             freq_buf[i] = NULL;

 

             if(i > 0)

             {

                          strrev(freq_buf);

                           *freq = atoi(freq_buf);

             }

 

             if(len == 0)

                           len = strlen(word);

 

             while(len > 0 && word[len] == '\n' || word[len] == ' ' || word[len] == '\t')

                           len--;

             if(len == 0)

                           word[0] = 0;

             else

                           word[len + 1] = 0;

             return TRUE;

}

 

  read_word 함수는 텍스트 파일에서 한 줄을 읽어 추천 검색어는 word에 빈도수는 freq에 저장한 후 TRUE를 리턴합니다.

 

 

bool comp_dsbl_word(DSBL_WORD& w1,DSBL_WORD& w2)

{

             if(strcmp(g_dwbuf.toStr(w1.dsbl_str),g_dwbuf.toStr(w2.dsbl_str)) > 0)

                           return false;

             else

                           return true;

}

 

int read_and_disassemble_words()

{

             FILE* word_list_fp;

             word_list_fp = fopen("word_list.txt","rt");

             char word[MAX_SGG_WORD_LEN];

             int freq;

             while(read_word(word_list_fp,word,&freq))

             {

                           if(word[0])

                           {

                                        char out[1024];

                                        dsbl_string(word,out,1024,NULL);

                                        DSBL_WORD w;

                                        w.org_str = g_wbuf.enqueue(word);

                                        w.dsbl_str = g_dwbuf.enqueue(out);

                                        w.freq = freq;

                                        g_wlist.push_back(w);

                           }

             }

             fclose(word_list_fp);

             sort(g_wlist.begin(),g_wlist.end(),comp_dsbl_word);

             return TRUE;

}

 

  read_and_disassemble_words while 문에서 추천 검색어와 빈도수를 읽어서 문자열은 자소 단위로 분리하여 DSBL_WORD 구조체에 값들을 입력한 후 벡터에 추가하는 것을 볼 수 있습니다. while 문을 다 돌고 나면 텍스트 파일의 모든 추천 검색어와 빈도수가 g_wlist라는 벡터에 저장이 됩니다.

 함수의 마지막 부분에 나타나는 sort 함수는 g_wlist의 원소들을 분리된 자소 순서대로 정렬하기 위해서 사용됩니다. 정렬을 하는 이유는 나중에 설명하도록 하겠습니다.

 텍스트에 있는 내용을 모두 메모리에 올렸으니, 이제 자동완성 기능에 필요한 형태로 데이터를 구축하는 일만 남았습니다. 데이터를 구축하는 방법에 대해 알아보기 전에, 힙과 B-TREE의 사용법에 대해서 간단히 알아보도록 하겠습니다. 힙에 대해선 따로 설명을 안 해드렸었네요. 그럼 먼저 힙에 대해서 알아보도록 하겠습니다.

 (heap)은 자료구조 중에 하나로, 역시 데이터를 정렬하는데 쓰입니다. 1000개의 정렬 가능한 데이터가 있다고 합시다. 그 중에서 우리는 상위 높은 정렬 값을 가지는 상위 10개의 데이터만 필요합니다. 만약 배열과 퀵소트 알고리즘을 이용한다면, 배열에 1000개의 데이터를 넣어두고, 모든 데이터를 정렬을 한 후 상위 10개의 데이터만을 가져올 수 있습니다. 하지만 상위 10개의 데이터만을 필요하는 상황에서 이것은 메모리와 CPU를 낭비하는 일입니다. 상위 10개를 제외한 990개의 데이터도 모두 정렬하기 때문이죠. 바로 이럴 때 사용하는 자료구조가 힙(heap)입니다. 힙에 데이터를 추가하면 상위 10개 까지만 데이터를 정렬 상태로 유지합니다. 1000개의 데이터를 힙에 모두 추가하면, 마지막에는 상위 10개의 데이터만 정렬되어서 남게 되죠. 물론 여기에서 말하는 데이터의 개수 10은 유동적인 것입니다. 힙이나 B-TREE를 실제 프로그램에서 사용하기 위해서는, 알고리즘을 연구해서 직접 만들어도 되겠지만, 오픈되어 있는 소스를 가져다 쓰는게 가장 무난합니다. 제가 쓰고 있는 소스도 물론 오픈되어 있는 소스를 가져다 약간 편집해서 쓰고 있는 것이구요. 그럼 이 프로그램에서 사용하고 있는 힙과 B-TREE의 사용법에 대해서 간단히 설명하도록 하겠습니다.

 힙을 사용하기 위해서는 먼저, heap.h 파일을 인클루드 하여야 합니다.

 그리고 다음과 같은 함수를 이용해 힙을 사용합니다. 각 함수에 대한 설명은 소스에 달려있는 주석으로 대체하도록 하겠습니다.

 

 /** 처음 힙을 초기화 시켜줄 때 호출한다.

             힙을 사용하기 전 반드시 호출하여야 한다.

             @param heap : HEAP 으로 선언한 변수의 주소를 넘겨준다.

             @param max_size : 힙에 들어갈 수 있는 최대 원소 개수

             @param compare : 각 원소들을 비교하는 함수 포인터

*/

void heap_init(HEAP *heap,int max_size,int (*compare)(const void *key1, const void *key2));

 

 

/** 다 사용한 힙을 해제한다.

*/

void heap_destroy(HEAP *heap);

 

 

/** 힙을 비운다.

             힙 안에 있는 원소의 개수를 0으로 초기화한다. 메모리는 해제하지 않는다.

*/

void heap_reset(HEAP *heap,int max_size,int (*compare)(const void *key1, const void *key2));

 

 

/** 힙에 원소를 추가한다.

*/

int heap_insert(HEAP *heap, void *data);

 

 

/** 힙에 모든 원소를 추가하였으면, 반드시 이 함수를 호출해서 마지막 정렬 작업을 하여야 한다.

*/

void heap_final(HEAP* heap);

 

 그럼 다음으로 B-TREE의 사용법에 대해서 알아보겠습니다.

 B-TREE를 사용하기 위해서는 flexible_bplus.h 파일을 인클루드 해줍니다.

 그리고 다음과 같은 함수들이 주로 쓰입니다. 여기서도 사용하고 있는 함수는 다음에 설명하고 있는 함수가 다 입니다.

 

// B-TREE 파일을 열 때 호출하는 함수입니다.

int open_index(char *,IX_DESC *, int);

 

// B-TREE 파일을 만들 때 호출하는 함수입니다.

int make_index(char *,IX_DESC *, int);

 

// 다 사용한 B-TREE 파일을 닫을 때 호출하는 함수입니다.

int close_index(IX_DESC *);

 

// B-TREE에 키를 추가할 때 사용합니다.

int add_key(void *, IX_DESC *);

 

// B-TREE에서 키를 찾을 때 사용합니다.

int find_key(void *, IX_DESC *);

 

 함수의 구체적인 사용법은 소스를 보면서 알아보겠습니다.

 지금까지 B-TREE와 힙의 사용법, 그리고 텍스트 파일의 추천 검색어를 벡터로 옮기는 과정까지 살펴보았습니다. 그럼 이제 자료를 적절히 구축하는 일만 남았네요. 크게 키 부분과, 데이터 부분으로 나눕니다.

 키 부분은 B-TREE를 사용하여, 자소 단위로 분리된 문자열이 들어갑니다. 예를 들어, 괴물, 감기 와 같은 추천 검색어가 있다고 하면, 이것은

각각 ㄱ ㅗ ㅣ ㅁ ㅜ ㄹ, ㄱ ㅏ ㅁ ㄱ ㅣ 와 같이 분리될 것입니다. 웹 브라우저에서 사용자가 을 입력한다면 위의 단어들이 모두 출력이 되어야 합니다. 그렇기 하기 위해서 키 부분에는 을 넣고 이 키와 연결된 데이터에는 출력할 문자열을 넣어둡니다. 바로 괴물, 감기가 되겠죠? 사용자가 ㄱㅗ를 입력하였을 때는 괴물이 출력이 되어야 합니다. 그래서 키 부분에 를 입력하고 키에 연결된 문자열에는 괴물을 넣어줘야 합니다. 괴물, 감기 두 단어에 대해서만 분석을 하였을 때는 다음과 같은 데이터가 구축이 됩니다.

 

키 부분

데이터 부분

ㄱㅏ

ㄱㅏㅁ

ㄱㅏㅁㄱ

ㄱㅏㅁㄱㅣ

ㄱㅗ

ㄱㅗㅣ

ㄱㅗㅣㅁ

ㄱㅗㅣㅁㅜ

ㄱㅗㅣㅁㅜㄹ

괴물,감기

감기

감기

감기

감기

괴물

괴물

괴물

괴물

괴물

 1

 

 B-TREE에 키는 ENTRY라는 구조체로 표현이 됩니다.

typedef struct                   

{ 

           RECPOS   idxptr;       /**  points to lower index level     */

           RECPOS   recptr;       /**  points to data record           */

           char     key[MAXKEY];/**  start of record key             */

}ENTRY;

 ENTRY 구조체는 위와 같은데, 여기에서 key 멤버 변수에 바로 위에서 설명한 자소 단위로 분리된 문자열이 들어갑니다. 그리고 recptr에는 원본 문자열이 저장되어 있는 위치가 저장됩니다. 원본 문자열도 하나의 파일에 저장이 되는데, 이 파일에는 실제 문자열이 들어가는 것이 아니라, 문자열은 또 다른 파일에 저장해 놓고, 그 위치만을 기록하고 있습니다. 이렇게 하는 이유는 데이터가 중복되기 때문에 공간의 낭비를 막기 위해서죠.

 자 그럼 다시 파일로 구분을 해서 알아보도록 하겠습니다.

 자동완성 기능에 필요한 자료 구축은 다음과 같은 3개의 파일로 이루어집니다.

 sgg_word_list.idx -> 위에서 설명한 B-TREE가 저장되는 파일입니다.

 sgg_word_list.dat -> 위의 표에서 데이터 부분이 저장되는 파일입니다.

 word_buf.dat -> 문자열의 내용이 저장되는 파일입니다.

 이 파일들의 구조는 예를 들어서 설명하는게 가장 좋을 것 같네요. 괴물, 감기, 가족 이 세 개의 단어를 가지고 자동완성 기능 데이터를 구축하면 위 파일들은 개략적으로 다음과 같은 내용을 갖습니다.

 

sgg_word_list.idx

 

 [ENTRY]entry_count

ENTRY = char key[1000], int data_point

entry_count : int, 자소 단위로 분리된 모든 추천 검색어 개수,  1에서의 entry_count 10 입니다.

data_point : int, sgg_word_list.dat에 표 1의 데이터 부분이 저장되는데, sgg_word_list.dat에 저장되는 위치를 나타낸다. 그림 1에서 화살표로 표현된다.

 

sgg_word_list.dat

 

 [WORD_LIST] entry_count

 WORD_LIST = nWord, [word_point]nWord

 

 nWord : int, 저장되는 word_point의 개수를 나타낸다.

 word_point : int, word_buf.dat 파일에 문자열이 저장되어 있는 위치를 가리킨다.

 

word_buf.dat

 

 [WORD]word_count

 WORD : 문자열(NULL 포함), 실제 추천검색어

 

 

그림 1

 

 

그림으로 나타내면 위와 같습니다.

그럼 위와 같이 생성한 데이터를 실제 어떻게 사용하는지 살펴보도록 하겠습니다. 예를 들어서, 사용자가 을 입력했을 때, sgg_word_list.idx 파일에서 이 있는지 찾아볼 것입니다. 있으면, sgg_word_list.dat 파일을 가리키고 있는 부분을 가져와서, sgg_word_list.dat의 파일 포인터를 방금 읽어온 위치로 변경합니다. 그리고, 4바이트 만큼 읽어서 int형 변수 nWord에 넣습니다. 그러면, nWord에는 사용자가 을 입력했을 때 추천해 줄 수 있는 추천 검색어의 개수가 들어가 있습니다. 위의 예에서는 3이 되겠죠? 그러면 이제 실제 추천 검색어가 저장되어 있는 위치를 3개만큼 읽어옵니다. 0, 5, 10이 되겠죠? 다시 word_buf.dat 파일의 0번째 위치에서 NULL을 만날때까지 문자열을 읽어옵니다. 바로 감기를 읽어오는 것입니다. 그 다음에는 word_buf.dat 5번째 위치에서 문자열을 읽어오면, 가족, 다음에는 괴물을 읽어올 수 있을 것입니다. 이렇게 읽어온 문자열을 출력해 주면 되는 것입니다. 개념적으로 설명하면 위와 같고, 실제 구현은 조금 다르게 되어 있습니다.

그러면, 실제 그림 1과 같이 데이터를 구축하는 소스를 보도록 하겠습니다.

소스에 달려있는 주석으로 설명을 대체하도록 하겠습니다.

 

 

/** 자동완성단어 리스트와 자소를 실제 파일에 쓴다.

@param dw_str : 자소단위로 분리된 단어

@param heap : dw_str에 제시되는 단어리스트, 단어의 내용이 저장되어 있는 위치가 저장된다.

@param ix : 자소단위로 분리된 단어가 기록되는 B-TREE

@param fp : 제시되는 단어리스트가 저장되는 파일 포인터

*/

int add_to_sgg_word_list(char* dw_str,HEAP* heap,IX_DESC* ix,FILE* fp)

{

             static int file_size = 0;

             ENTRY e;

             strcpy(e.key,dw_str);

             e.recptr = file_size;

             add_key(&e,ix);

             for(int i=0;isize;i++)

             {

                           int index = (int)heap->tree[i];

                           DSBL_WORD& w = g_wlist[index];

                           g_word_indice[i] = w.org_str;

             }

             fwrite(&heap->size,sizeof(int),1,fp);

             fwrite(g_word_indice,sizeof(int),heap->size,fp);

             file_size += sizeof(int) + sizeof(int) * heap->size;

             return TRUE;

}

 

 

/** 생성된 g_wlist 를 이용해, 자동완성단어 리스트를 만든다.

   "ㄱㅏㄴ" 이런 부분이 키 부분이 되고, 데이터 부분은 왼쪽의 자소를 포함하고 있는 단어들 중

   빈도수가 가장 높은 N개를 뽑아, 이 단어들이 g_wlist에서 갖는 인덱스 리스트가 된다.

   데이터부분은 N(int), index(int) * N 와 같은 형식으로 저장된다.

   키를 저장하는 파일이 sgg_word_list.idx 이고, 데이터를 저장하고 있는 파일이 sgg_word_list.dat

   이다. 이 함수를 호출하면, 두 개의 파일이 새로 생성된다.

*/

int make_suggest_word_list()

{

             HEAP heap;

             IX_DESC ix;

             make_index("sgg_word_list.idx",&ix,FALSE);

             FILE* fp = fopen("sgg_word_list.dat","wb");

 

             heap_init(&heap,MAX_SGG_WORD,comp_sgg_word);

             int size = g_wlist.size();

             //단어리스트에 있는 모든 단어에 대해여...

             for(int i=0;i

             {

                           int mlen;

                           //dw에는 i번째 단어의 자소가 들어간다.

                           char* dw = g_dwbuf.toStr(g_wlist[i].dsbl_str);

                           printf("%s %d\n",g_wbuf.toStr(g_wlist[i].org_str),g_wlist[i].freq);

                           if(i == 0)

                                        mlen = 0;

                           else

                                        mlen = get_match_len(g_dwbuf.toStr(g_wlist[i-1].dsbl_str),dw);

                           int dwlen = strlen(dw);

                           for(int len = mlen+1;len <= dwlen;len++)

                           {

                                        heap_reset(&heap,0,NULL);

                                        for(int j = i; j < size;j++)

                                        {

                                                     //dw2에는 j번째 단어의 자소가 들어가는데, j i에서부터 size까지이다.

                                                     // dw2 dw를 포함하고 있을때까지 j가 증가한다.

                                                     //다시 말하면, dw를 포함하는 단어를, dw에 대한 제시어 후보 리스트에 추가한다고 생각하면 된다.

                                                     char* dw2 = g_dwbuf.toStr(g_wlist[j].dsbl_str);

                                                     if(!memcmp(dw,dw2,len))

                                                                  heap_insert(&heap,(void*)j);

                                                     else

                                                                  break;

                                        }

                                        heap_final(&heap);

                                        char temp = dw[len];

                                        dw[len] = NULL;

                                        add_to_sgg_word_list(dw,&heap,&ix,fp);

                                        dw[len] = temp;

                           }

             }

             heap_destroy(&heap);

             g_wbuf.write("word_buf.dat");

             close_index(&ix);

             fclose(fp);

             return TRUE;

}

 

 여기까지, 데이터를 어떻게 구축해야 되는지에 대해서 알아보았습니다.

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함