/**
*作者:夺天策 百度空间名:刹那剑欣
*转载请说明出处!
*/
这几天完成了我的中文分词算法,就着手把它加入到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,完毕。。。。