본문 바로가기

Lucene

[lucene] CharTokenizer를 살펴보자.

한글 색인어 추출기 작업을 위해서 가장 먼저 살펴봐야 했던 class가

 CharTokenizer였다.

정말 아무것도 모르는 상태에서 시작하게 된거라서 난감하기도 했고

오픈소스라는 놈을 처음 열어보는거라..(그냥 쓰기만했지... 이걸 까볼줄은...;;)

CharTokenizer를 열어보았을때 느낌은..

"이 뭥미?"

이해가 전혀 되지 않았다 -_-

그냥 일단 한손에 삽들고 팠다.


CharTokenizer는 루씬에서 모든 Analyzer의 기본이 된다.

이놈의 역할은 문장을 읽어들여서 Token을 만들어 반환하는 것이다.

그럼 어떻게 Token을 만드느냐..

한글자씩 읽어와서 문자냐 아니냐를 판단하여 만들어내는데 그 역할 하는 것이

protected abstract boolean isTokenChar(char c);

이 메서드이다. 추상클래스이기 때문에 구현을 해줘야 하는데 기본적으로 제공되는

SimpleAnalyzer를 보면 아래와 같이 구현되어 있다.

  protected boolean isTokenChar(char c) {
    return Character.isLetter(c);
  }

문자냐 아니냐....

자 그럼 대충 감이 올것이다.

"안녕하세요?용식입니다."
"안녕하세요 용식입니다."
"안녕하세요yongsik용식입니다."
"안녕하세요12345용식입니다."

위에서부터 1~4번이라 하고
1번을 한글자씩 읽어오면.. [안녕하세요]까지 읽어온 후 ?가 나오는 시점에서
문자가 아니기 때문에 [안녕하세요]까지가 하나의 토큰이 된다.

2번은 스페이스가 있는 부분에서 토큰으로 나뉘어진다.
3번은? 전부 문자이기 때문에 [안녕하세요yongsik용식입니다]가 하나의 토큰이 되고
4번은 숫자가 빠지고 [안녕하세요] [용식입니다]가 된다.

그리고 또하나의 메서드가 있는데

 protected char normalize(char c) {
    return c;
  }
메서드이다. 정규화? 응?

영어로 검색을 할 경우  abc로 검색하는 경우와 ABC로 검색하는 경우에 결과가 서로
달라서야 쓰겠는가!

일반적으로 저 메서드는 아래와 같이 오버라이드 되어있다.

  protected char normalize(char c) {
    return Character.toLowerCase(c);
  }

소문자...

자 여기까지 봤을때 느껴지는 것이 있을것이다. 바로..

"영어권에서는 꽤... 결과가 좋겠네.. 영어에서 유용하겠네.." 이다.

영어는 일반적으로 단어마다 스페이스가 들어가있고, 대소문자가 다르기에 저런 정규화과정이 필요할 것이다. 한글을 처리하기 위해서는 우리에게 맞게 이 클래스를 수정해 주어야 한다.

처음에 나는 상품명을 대상으로 색인어를 추출 할 생각이었다.
(지금은 복합명사추출까지...)

일반적인 상품명을 보면..

"나이키 청바지", "dvd플레이어 모델명 mlkd-112009" , "삼성mp3 yepp-1011" 등으로 나누어지는데

일단 스페이스를 기준으로 분리를 하면 어느정도 모양새는 갖춰진다.
(http://devyongsik.tistory.com/21 참조)

하지만 위에서 3,4번의 예를 봤듯이 영문과 한글을 떨어트려야 하고
영문과 숫자는 붙여놓아야 그나마 좀 더 제대로 된 상품명과 모델명을 가져 올 수가 있게 된다.
(dvd플레이어 -> dvd, 플레이어, mp3 -> mp3)

그렇다면 어떻게 하면 좋을라나...

CharTokenizer에서 한글한글자 읽어올때 그 character의 값을 비교하여
토큰으로 분리시켜 낼 것인지 아닌지를 판단해서  return 해주면 되는 것이다.

이를 위해서 isTokenChar를 오버라이드 했고,
next(Token token) 메서드를 오버라이드 했다.

 protected boolean isTokenChar(char c) {
  return (Character.isLetter(c) || Character.isDigit(c) || (c == '-'));
 }

일단 이렇게 해놓으면 숫자가 걸러지는 일도 없고 모델명을 위해서 -도 토큰에 들어가게 된다.

그리고  next(Token) 메서드에 저 c를 비교하여 토큰을 끊는 로직을 넣어주면 되는 것이다.


이런식으로 대상이 되는 데이터에 따라서 조금씩 클래스를 수정하여 입맛대로 (어느정도) 바꿔낼 수 가 있다. (블로그같은 광범위한 데이터라면 많이 복잡해 질테지만..)

아무튼 일단 이렇게 토큰을 잘라내고나서 이제 Filter에서

어미제거/불용어제거/복합명사추출/이름추출/동의어추출 등의 작업을 하게 된다.