소프트웨어/실용주의 프로그래머

형태소 분석기에 유니코드 모듈 추가

falconer 2008. 7. 30. 14:33

운동하고 집에와서 잠시 게임으로 긴장을 풀어준 다음에 바로 코딩에 들어갔다.

간만에 하는 C언어 코딩이지만 풋~~~ 역시나 나름의 맛이 있는거 같다. ㅎㅎ



오랜만에 형태소 분석기 코딩을 하는 이유는.....???.



typos님 께서 공개된 형태소 분석기가 필요하다고 해서 썩혀 두려고 한 분석기 소스를 오랜만에 훓어 보았다. 게다가 역방향 복합명사 분해 알고리즘을 실제 적용해 보고 싶은 욕구가 불끈 솥아 올랐던것도 한몫 하기도 했다.(물론 Lucene에 붙이기 위해서이지만 14만 한국어 사전의 취약점 때문에 굉장히 망설였다.)



일단 Lucene이 유니코드 기반의 문자셋을 이용하니 그동안 완성형, 조합형 기반의 분석기 소스에 유니코드 변환 모듈을 넣을 필요가 있었다.



iconv라는 라이브러리를 쓰려고 했지만 유니코드가 어떤식으로 돌아가는지 알고 싶기도 하고 직접 손으로 만드는걸 좋아하는지라 한번 만들어 봤다.

'완성형 -> 유니코드', '조합형 -> 유니코드' 의 기능을 구현 했는데,

유니코드 변환을 하는건 '조합형 <-> 완성형' 방법하고 크게 다르지 않았다.

대강 만드는 방법을 소개하도록 하겠다.(Lucene in Action에서 테이블을 살짝 빌려왔다.ㅋ)

'조합형 -> 유니코드'의 변환은 아래 테이블을 참조하면 금방 만들 수 있다.

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
유니코드 한글 초성 배열






0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
유니코드 한글 중성 배열




(없음)
0 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
유니코드 한글 종성 배열

한 글자의 유니코드 값 계산 법

= 0xAC00(유니코드 한글 시작점) + [초성번호]*588 + [중성번호]*28 + [종성번호]

예를들어 '가'문자의 코드를 찾으려면 공식을 이용해 찾을 수 있다.

가 = 0xAC00 + 0(초성ㄱ)*588 + 0(중성ㅏ)*28 + 0(종성(없음)) = 0xAC00

조합형의 코드표는 아래와 같다.

비트조합 (bit) 10진 코드 16진 코드 초성(순번) 중성(순번) 종성(순번)
0 0 0 0 0 0 00 미정의 미정의 미정의
0 0 0 0 1 1 01 채움 미정의 채움
0 0 0 1 0 2 02 ㄱ (0x00) 채움 ㄱ (0x00)
0 0 0 1 1 3 03 ㄲ (0x01) ㅏ (0x00) ㄲ (0x01)
0 0 1 0 0 4 04 ㄴ (0x02) ㅐ (0x01) ㄳ (0x02)
0 0 1 0 1 5 05 ㄷ (0x03) ㅑ (0x02) ㄴ (0x03)
0 0 1 1 0 6 06 ㄸ (0x04) ㅒ (0x03) ㄵ (0x04)
0 0 1 1 1 7 07 ㄹ (0x05) ㅓ (0x04) ㄶ (0x05)
0 1 0 0 0 8 08 ㅁ (0x06) 미정의 ㄷ (0x06)
0 1 0 0 1 9 09 ㅂ (0x07) 미정의 ㄹ (0x07)
0 1 0 1 0 10 0A ㅃ (0x08) ㅔ (0x05) ㄺ (0x08)
0 1 0 1 1 11 0B ㅅ (0x09) ㅕ (0x06) ㄻ (0x09)
0 1 1 0 0 12 0C ㅆ (0x0a) ㅖ (0x07) ㄼ (0x0a)
0 1 1 0 1 13 0D ㅇ (0x0b) ㅗ (0x08) ㄽ (0x0b)
0 1 1 1 0 14 0E ㅈ (0x0c) ㅘ (0x09) ㄾ (0x0c)
0 1 1 1 1 15 0F ㅉ (0x0d) ㅙ (0x0a) ㄿ (0x0d)
1 0 0 0 0 16 10 ㅊ (0x0e) 미정의 ㅀ (0x0e)
1 0 0 0 1 17 11 ㅋ (0x0f) 미정의 ㅁ (0x0f)
1 0 0 1 0 18 12 ㅌ (0x10) ㅚ (0x0b) 미정의
1 0 0 1 1 19 13 ㅍ (0x11) ㅛ (0x0c) ㅂ (0x10)
1 0 1 0 0 20 14 ㅎ (0x12) ㅜ (0x0d) ㅄ (0x11)
1 0 1 0 1 21 15 미정의 ㅝ (0x0e) ㅅ (0x12)
1 0 1 1 0 22 16 미정의 ㅞ (0x0f) ㅆ (0x13)
1 0 1 1 1 23 17 미정의 ㅟ (0x10) ㅇ (0x14)
1 1 0 0 0 24 18 미정의 미정의 ㅈ (0x15)
1 1 0 0 1 25 19 미정의 미정의 ㅊ (0x16)
1 1 0 1 0 26 1A 미정의 ㅠ (0x11) ㅋ (0x17)
1 1 0 1 1 27 1B 미정의 ㅡ (0x12) ㅌ (0x18)
1 1 1 0 0 28 1C 미정의 ㅢ (0x13) ㅍ (0x19)
1 1 1 0 1 29 1D 미정의 ㅣ (0x14) ㅎ (0x1a)
1 1 1 1 0 30 1E 미정의 미정의 미정의
1 1 1 1 1 31 1F 미정의 미정의 미정의





그래서 조합형 테이블을 약간만 이용하면 조합형과 유니코드의 mapping 배열을 만들 수 있다. 그러니까 조합형 음소값이 인덱스 값이 되고 유니코드의 조,중,종성변호가 값이 되는것이다.



이것들을 이용해서 종성의 코드변환 테이블을 만들어 보자면...



/* 조합형 한글  중성 -> 유니코드 종성으로 변환 */

static unsigned int johab_lcon_to_uni[] = {
    0, 0,                      /* (미정의), (채움) */
    1, 2, 3, 4,                /* ㄲ, ㄳ, ㄴ */
    5, 6, 7, 8, 9,             /* ㄵ, ㄶ, ㄷ, ㄹ, ㄺ */
    10, 11, 12, 13, 14,        /* ㄻ, ㄼ, ㄽ, ㄾ, ㄿ */
    15, 16, 0, 17, 18,         /* ㅀ, ㅁ, (미정의), ㅂ, ㅄ */
    19, 20, 21, 22, 23,        /* ㅅ, ㅆ, ㅇ, ㅈ, ㅊ */
    24, 25, 26, 27             /* ㅋ, ㅌ, ㅍ, ㅎ */
};


이런식으로 초성, 중성에 대한 테이블을 만들어 주고 적절하게 문자를 표현할 방법만 찾으면 '조합형->유니코드'로의 변환할 수 있는 기능을  쉽게 만들 수 있을것이다.



그리고 '완성형 -> 유니코드'로의 변환 방법은 코드자체를 맵핑 시키는 테이블이 있어야 한다. 그 테이블과 소스는 paraon님의 블로그에 상세하게 소스채로 올라와 있으니 참고 하길 바란다.



하여튼 이번 여름 휴가때 고향에서 형태소 분석기나 Lucene버전으로 포팅을 해봐야 겠다.

간단하게 체언에서 조사만 정확하게 떼어내도 성과가 괜찮을듯 한데 용언은 할수 있는 불규칙 처리만 하도록 하고....말이지....



군산대학교 랜을 몰래 쓸 수 있으려나 모르겠다. 일단 환경 조사를 해야겠음이군.


출처 : http://freesearch.pe.kr/485