算法和数据结构分析:
由于Analysis包比较简单,不详述了!
算法:基于机械分词 1-gram,2-gram,HMM(如果使用ICTCLAS接口的话)
数据结构:部分源码用到了Set ,HashTable,HashMap
认真理解Token
Lucene中的Analysis包专门用于完成对于索引文件的分词.Lucene中的Token是一个非常重要的概念.
看一下其源码实现:
public final class Token {
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)
public Token(String text, int start, int end, String typ)
public void setPositionIncrement(int positionIncrement)
public int getPositionIncrement() { return positionIncrement; }
public final String termText() { return termText; }
public final int startOffset() { return startOffset; }
public void setStartOffset(int givenStartOffset)
public final int endOffset() { return endOffset; }
public void setEndOffset(int givenEndOffset)
public final String type() { return type; }
public String toString()
}
下面编一段代码来看一下
TestToken.java
package org.apache.lucene.analysis.test;
import org.apache.lucene.analysis.*;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import java.io.*;
public class TestToken
{
public static void main(String[] args)
{
String string = new String("我爱天大,但我更爱中国");
//Analyzer analyzer = new StandardAnalyzer();
Analyzer analyzer = new TjuChineseAnalyzer();
//Analyzer analyzer= new StopAnalyzer();
TokenStream ts = analyzer.tokenStream("dummy",new StringReader(string));
Token token;
try
{
int n=0;
while ( (token = ts.next()) != null)
{
System.out.println((n++)+"->"+token.toString());
}
}
catch(IOException ioe)
{
ioe.printStackTrace();
}
}
}注意看其结果如下所示
0->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(我,0,1,<CJK>,1)
1->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(爱,1,2,<CJK>,1)
2->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(天,2,3,<CJK>,1)
3->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(大,3,4,<CJK>,1)
4->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(但,5,6,<CJK>,1)
5->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(我,6,7,<CJK>,1)
6->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(更,7,8,<CJK>,1)
7->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(爱,8,9,<CJK>,1)
8->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(中,9,10,<CJK>,1)
9->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(国,10,11,<CJK>,1)
注意:其中”,”被StandardAnalyzer给过滤掉了,所以大家注意第4个Token直接startOffset从5开始.
如果改用StopAnalyzer()
0->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(我爱天大,0,4,word,1)
1->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(但我更爱中国,5,11,word,1)
改用TjuChineseAnalyzer(我写的,下文会讲到如何去写)
0->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(爱,3,4,word,1)
1->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(天大,6,8,word,1)
2->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(更,19,20,word,1)
3->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(爱,22,23,word,1)
4->Token's (termText,startOffset,endOffset,type,positionIncrement) is:(中国,25,27,word,1)
讲明白了Token,咱们来看以下其他的东西
一个TokenStream是用来走访Token的iterator(迭代器)
看一下其源代码:
public abstract class TokenStream {
public abstract Token next() throws IOException;
public void close() throws IOException {}
}
一个Tokenizer,is-a TokenStream(派生自TokenStream),其输入为Reader
看一下其源码如下:
public abstract class Tokenizer extends TokenStream {
protected Reader input;
protected Tokenizer() {}
protected Tokenizer(Reader input) {
this.input = input;
}
public void close() throws IOException {
input.close();
}
}
一个TokenFilter is–a TokenStream(派生自TokenStream),其义如名就是用来完成对TokenStream的过滤操作,譬如
去StopWords,将Token变为小写等。
源码如下:
public abstract class TokenFilter extends TokenStream {
protected TokenStream input;
protected TokenFilter() {}
protected TokenFilter(TokenStream input) {
this.input = input;
}
public void close() throws IOException {
input.close();
}
}
一个Analyzer就是一个TokenStream工厂
看一下其源码就:
public abstract class Analyzer {
public TokenStream tokenStream(String fieldName, Reader reader)
{
return tokenStream(reader);
}
public TokenStream tokenStream(Reader reader)
{
return tokenStream(null, reader);
}
}
好,现在咱们来看一下Lucene的Analysis包下面的各个类文件都是用来干什么的。按照字典排序。
Analysis包中的源码详解
Analyzer.java 上文已经讲过。
CharTokenizer.java 此类为简单一个抽象类,用来对基于字符的进行简单分词(tokenizer)
LetterTokenizer.java两个非字符之间的字符串定义为token(举例来说英文单词由空白隔开,那个两个空白之间的字符串即被定义为一个token。备注:对于绝大多数欧洲语言来说,这个类工作效能很好。当时对于不用空白符分割的亚洲语言,效能极差(譬如中日韩)。)
LowerCaseFilter.java is-a TokenFilter用于将字母小写化
LowerCaseTokenizer is-a Tokenizer功能上等价于LetterTokenizer+LowerCaseFilter
PerFieldAnalyzerWrapper是一个Analyzer,因为继承自Analyzer当不同的域(Field)需要不同的语言分析器(Analyzer)时,这个Analyzer就派上了用场。使用成员函数addAnalyzer可以增加一个非缺省的基于某个Field的analyzer。很少使用。
PorterStemFilter.java使用词干抽取算法对每一个token流进行词干抽取。
PorterStemmer.java 有名的P-stemming算法
SimpleAnalyzer.java
StopAnalyzer.java 具有过滤停用词的功能
StopFilter.java StopFilter为一个Filter,主要用于从token流中去除StopWords
Token.java 上面已讲.
TokenFilter.java 上面已经讲了
Tokenizer.java 上面已经讲了
TokenStream.java 上面已经讲了
WhitespaceAnalyzer.java
WhitespaceTokenizer.java 只是按照space区分Token.