前面我们分析了一下Tokenizer类及它的子类,Tokenizer类继承于TokenStream类,它的作用主要是把一个字符串分隔成一个个的词,不同的子类实现不同的切分方式。有按空格的,有按非英文字符的。把切分出来的词Token组合成TokenStream。今天我们要讨论的是TokenFilter及其子类。TokenFilter类也是继承于TokenSteam类,它的作用是对分出来的词进行一些处理,比如去掉停词,转换大小写。我们来看TokenFilter类的代码:
abstract public class TokenFilter : TokenStream
{
///<summary>
///需要传过来的处理的TokenStream
///The source of tokens for this filter.
///</summary>
protected TokenStream input;
///<summary> Close the input TokenStream. </summary>
public override void Close()
{
input.Close();
}
}
也是一个抽象类,它里面定义了一个受保护的成员TokenStream类型的input,我们知道在Tokenizer类里面定义了一个受保护的TextReader类型的input,Tokenizer类里面接收的是一个文本流,而TokenFilter类则需要接收一个TokenStream,也就是一个语汇单元流。具体的流程就是把一个字符串传递给Tokenizer,Tokenizer把这个字符串分拆成一个个的Token,然后把这些Token组合成一个TokenStream,也就是一个语汇单元流,然后把这个语汇单元流传给TokenFilter,TokenFilter会对TokenStream里面的每一个Token进行一下处理。根据处理方式的不同,由不同的子类来实现这些处理方式。先来看看第一个子类LowerCaseFilter,也就是把每个Token都转换成小写的。
public class LowerCaseFilter : TokenFilter
{
/// <summary>
/// Initializes a new instance of the LowerCaseFilter class.
/// </summary>
/// <param name="ts">Token stream to read from.</param>
public LowerCaseFilter(TokenStream ts)
{
input = ts;
}
/// <summary>
/// Returns the next token from the stream.
/// </summary>
/// <returns>The next token or null if EOS.</returns>
public override Token Next()
{
Token t = input.Next();
if (t == null)
return null;
t.TermText = t.TermText.ToLower();
return t;
}
}
主要就是Next方法,里面就是把每一个Token转换成小写。方法比较简单,就不用解释了。再看第二个子类,去掉停词的类StopFilter,我们就只看Next方法,代码如下:
public override Token Next()
{
// return the first non-stop word found
for (Token token = input.Next(); token != null; token = input.Next())
if (table[token.TermText] == null)
return token;
// reached EOS -- return null
return null;
}
这个方法里面有一个table变量,它是一个HashTable的对象,里面装的就是停词,也就是那些没有实际意义,需要剔除的词,这个停词集合,在这个类里面有一个初始化,也就是英文中常用的停词集合,你也可以自己传入一个停词的集合。Next方法主要就是遍历每一个Token,看其是否是停词,如果是的,就去掉。
接下来要看的一个子类就比较有意思,它的作用就是把词元进行一些Stem处理,Stem就是把每一个词元还原成原型,比如cars转换成car。这里会用到一个著名的的stemming算法,全称是The Porter Stemming Algorithm,其主页为http://tartarus.org/~martin/PorterStemmer/,也可查看其论文http://tartarus.org/~martin/PorterStemmer/def.txt。通过以下网页可以进行简单的测试:Porter's Stemming Algorithm Online[http://facweb.cs.depaul.edu/mobasher/classes/csc575/porter.html]。比如cars –> car,driving –> drive,tokenization –> token。下面我们来看看这个类的Next方法:
public override Token Next()
{
Token token = input.Next();
if (token == null)
return null;
else
{
String s = stemmer.stem(token.TermText);
if (s != token.TermText) // Yes, I mean object reference comparison here
token.TermText = s;
return token;
}
}
当然首先是定义了一个封装了stemming算法的类PorterStemmer的对象stemmer,然后调用这个里面的stem方法,把每一个Token都处理一下,返回即可。有时间的话把stemming算法单独拿出来解读一下。
分词的一些基本类都已经介绍完成了,前面所介绍的都是一些简单的分词,Lucene里面内置了四个分词器,分别是WhitespaceAnalyzer,SimpleAnalyzer,StopAnalyzer还有一个StandardAnalyzer分词器,前三个分词器用前面介绍的一些类就可以完成其功能,StandardAnalyzer分词器比较复杂,下次将来解读,构成StandardAnalyzer分词器的StandardFilter类和StandardTokenizer类。