浙江省高等学校教师教育理论培训

微信搜索“毛凌志岗前心得”小程序

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
http://hi.baidu.com/chanajianxin/blog/item/9b36608bb7dfc013c8fc7a9c.html

改写lucene的Analyzer,添加自己的中文分词系统的方法(原创)
2007-04-30 16:16

/**
*作者:夺天策      百度空间名:刹那剑欣
*转载请说明出处!   
*/

    这几天完成了我的中文分词算法,就着手把它加入到lucene中去,google,baidu一下,倒是有一些人写的中文分词,和加入的方法,但是那些都 是符合他们自己写的分词算法的添加方法,没有讲到lucene的添加接口,没有将原理,于是就自己研究了下咯,看了下lucene的源代码,总结出方法, 希望对现在还不知道的朋友会有帮助!!

源代码中主要分词器都在analysis 这个包中,在contrib中还有一些第三方的分词器,有一个中文分词器ChineseAnalyzer.java,但是他只是实现了二分(没两个汉字算作一个Token或者Term)方法的分词,对实际应用显然不能满足

所有的Analyzer都有一个抽象的父类Analyzer
lucene源代码:public abstract class Analyzer
每个子类的Analyzer都继承与这个类,并且实现这个类的tokenStream方法
lucene源代码:public abstract TokenStream tokenStream(String fieldName, Reader reader);
这个方法返回的是一个TokenStream实例(其实不是TokenStream实例,因为下面会开到TokenStream其实是一个抽象类,这里应该说是返回实现这个抽象类的实例)。要了解这个类,必须去看看这个类的源代码
TokenStream类:
public abstract class TokenStream {
     /** Returns the next token in the stream, or null at EOS. */
     public abstract Token next() throws IOException;

     /** Releases resources associated with this stream. */
     public void close() throws IOException {}
}

我们可以看到,这个类中有两个方法,主要的是一个next()方法,这个方法没调用一次反回一个Token实例,要想了解Token类,必须再看lucene的源代码咯

由于源代码有点长,贴出最核心的几个变量和方法
String termText;         // the text of the term
     int startOffset;         // start in source text
     int endOffset;         // end in source text
     String type = "word";         // lexical type
     private int positionIncrement = 1;
public Token(String text, int start, int end) {
       termText = text;
       startOffset = start;
       endOffset = end;
     }
public Token(String text, int start, int end, String typ) {
       termText = text;
       startOffset = start;
       endOffset = end;
       type = typ;
     }
。。。。。。

我们看到一个Token是有一个TermText,startOffset,endOffset,type,positionIncrement.....等组成,有两个构造函数。大体的结构就是这样了。下面我们看看个lucene现成的Analyzer的例子:SimpleAnalyzer.java


public final class SimpleAnalyzer extends Analyzer {
     public TokenStream tokenStream(String fieldName, Reader reader) {
       return new LowerCaseTokenizer(reader);
     }
}

我们看到他继承了Analyzer并且重写了方法tokenStream,返回一个TokenStream,我们知道TokenStream是一个 抽象的类,LowerCaseTokenizer也是继承了这个抽象类,所以lucene可以这样返回一个LowerCaseTokenizer当作是 TokenStream,这里我们不再继续通过LowerCaseTokenizer分析下去了,因为LowerCaseTokenizer又是继承与 TokenFilter,而TokenFilter又是继承 TokenStream,有点乱。呵呵,我们这里只需要了解,新建的Analyzer需要继承Analyzer这个类,并且实现一个返回 TokenStream的tokenStream方法,而通过上面的分析,我们知道,我们只需要写一个类继承与TokenStream,并且实现其中 next()方法,比如我们起名为:KinggouTokenStream,这个类源代码如下:

public class KinggouTokenStream extends TokenStream{

public KinggouTokenStream(Reader reader){....//把reader对象变成String,并且在next方法中每次将一个词组封装为Token类型,然后返回!}

/**这个方法很关键,你每次next都必须返回一个 Token实例(在上面我有谈到),这里你可以在next方法中,实例化一个Token,并且把其中的TermText赋值为 分词出来的词组,然后startOffset和endOffset其实是这个次在整个token流中的首位置和尾位置,你可以通过分词得到的词组的 length去加基数去实现。
**/
public Token next() throws IOException{...............}

}

kinggouTokenStream 完成了,接下类写真正的Analyzer类,我们起名为KinggouAnalyzer,源代码如下:

public class kinggouAnalyzer extends Analyzer {

public TokenStream tokenStream(String filename, Reader reader) {
      //你需要在kinggouTokenStream中把这个reader对象读取为String,并且分词,然后为next方法定 义每次返回一个Token对象
         return new kinggouTokenStream(reader)
}
}

以上类都是便于理解写的,不一定保证能编译通过,只是写了主要的方法!
附上kinggou的写法:

--------------下面的是KinggouTokenStream,具体项目中我用SegmentOutInterface代替了-----------------

/**
* the interface of Segment to outside
* extend org.apache.lucene.analysis.TokenStream
* method: next()     return Token
* 分词对外的接口,继承 lucene的TokenStream
*/

package com.kinggou.engine.segment;
import java.io.IOException;
import java.util.StringTokenizer;

import org.apache.lucene.analysis.Token;
import org.apache.lucene.analysis.TokenStream;

//SegmentOutInterface相当与上面的kinggouTokenStream

public class SegmentOutInterface extends TokenStream {
int start=0;
int end=0;
String list;
StringTokenizer st;
public SegmentOutInterface(String str){
//这里是分词的实现,分词实现有我写的一个具体的包,这里不写出
     list=WordSegment.getInstance().Segment(str, " ");;
     st=new StringTokenizer(list);
}
String temp;
Token tk;

/**
     * 实现的核心方法
     * */
@Override
public Token next() throws IOException {
   if(st.hasMoreTokens()){
    temp=st.nextToken();
    end=start+temp.length();
    tk=new Token(temp,start,end);
    start=end+1;
    //end=end+temp.length();
   }else{
    tk=null;
   }
   return tk;
}
}

---------------------------------下面的是kinggouAnalyzer----------------------

package com.kinggou.engine.index;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import com.kinggou.engine.segment.SegmentOutInterface;

public class kinggouAnalyzer extends Analyzer {

/**
     * 改写的lucene自己的分析器
     * */
@Override
public TokenStream tokenStream(String filename, Reader reader) {
     StringBuffer str=new StringBuffer();
     BufferedReader br=new BufferedReader(reader);
     String temp="";
     try {
      temp = br.readLine();
     } catch (IOException e) {
      e.printStackTrace();
     }
     while(temp!=null){
      str.append(temp);
      try {
       temp=br.readLine();
      } catch (IOException e) {
       e.printStackTrace();
      }
     }
     return new SegmentOutInterface(str.toString());
}

}

ok,完毕。。。。



posted on 2011-09-30 20:30  lexus  阅读(724)  评论(2编辑  收藏  举报