原文出处:
http://lqgao.spaces.live.com/blog/cns!3BB36966ED98D3E5!437.entry?_c11_blogpart_blogpart=blogview&_c=blogpart#permalink
本文主要分析一下 Lucene输入部分——Analyzer(分析器)。为什么要有Analyzer部分呢?打个比方,人体在消化食物的时候,是不是都要把食物分解掉?食物在肠道里面,被分解成葡萄糖、氨基酸、脂肪等等。变成小块以后,才容易被吸收并加以利用。Lucene也有类似的过程:把文本分解成更小的单元,有词、标点符号、分割符号,甚至还有网站名等等。Analyzer就好比是人体的肠道,它的职责就是把输入的文本切成小的单元。
先看一段代码吧:
#0001
#0002
#0003
#0004
#0005
#0006
#0007
#0008
#0009
#0010
#0011
#0012
#0013
#0014
#0015
#0016
#0017
#0018
#0019
#0020
#0021
#0022
#0023
#0024
#0025
#0026
运行结果如下:
Lucene.Net.Analysis.WhitespaceAnalyzer :
[This][is][a][test][document.][For][more][info,][please][visit][Victor's][Blog:]
[http://lqgao.spaces.msn.com.]
Lucene.Net.Analysis.SimpleAnalyzer :
[this][is][a][test][document][for][more][info][please][visit][victor][s][blog][h
ttp][lqgao][spaces][msn][com]
Lucene.Net.Analysis.StopAnalyzer :
[test][document][more][info][please][visit][victor][blog][http][lqgao][spaces][m
sn][com]
Lucene.Net.Analysis.Standard.StandardAnalyzer :
[test][document][more][info][please][visit][victor][blog][http][lqgaospacesmsnco
m]
好,让咱们来分析一下。Lucene中默认提供4个Analyzer:SimpleAnalyzer, StandardAnalyzer, StopAnalyzer, WhitespaceAnalyzer。至于这4个有什么区别,听我慢慢道来。
WhitespaceAnalyzer似乎什么都不做,就是按照white space (空格符号)把文本分开——这样做最省力,最简单。
SimpleAnalyzer 则比WhitespaceAnalyzer进步一些,至少不管大写还是小写的字母,统统变成小写形式。这样做的好处也很明显,不管输入是This tHis还是 THIS thIS,最后都统一为this,便于匹配。除了统一大小写外,SimpleAnalyzer还把标点符号处理了,或者说SimpleAnalyzer是按照标点符号分割单词的。比如`documents.’在SimpleAnalyzer的结果中变为`document’。
StopAnalyzer 看起来和SimpleAnalyzer非常相似,只不过,结果中有一些词被去掉了,比如‘this’, ‘is’, ‘a’, ‘for’等——这些大量出现但没有实际意义的词通常被称为stop word(停用词),并被去掉,不加入索引中。因为这样的词数量很大,但并不能很好的区分文档的内容。去掉stop word能减少索引的规模。
StandardAnalyzer做得要复杂一些了。像”Victor’s”这样的词,被处理为’victor’,并没有”’s”,而且网址也被处理了。稍后我们分析StandardAnalyzer的功能。这几个Analyzer的继承关系如图 1所示。
图 1几种analyzer的类层次图
现在回头再看看Analyzer们是怎么工作的(#0015~#0026)。其实Analyzer是一个工厂模式(Factory Pattern),见#0017。使用时需要其生成一个TokenStream的对象。TokenStream,顾名思义,表示token流,即一个 token序列。每个token都是Token类型的。#0020~#0023展现TokenStream的调用方式。
接下来让我们一步一步地展开Analyzers的细节。既然Token是TokenStream组成的元素,让我们先来看看它的“庐山真面目”。
#0001
#0002
#0003
#0004
#0005
#0006
#0007
#0008
#0009
#0010
#0011
#0012
#0013
#0014
#0015
#0016
#0017
#0018
#0019
可以看出,Token存储了term的字符串(#0003),并记录下起始和终止位置(#0004~#0005),此外还有一个类型信息(#0006)。DumpAnalyzer中调用了TermText()获取字符串信息。
然后看看TokenStream:
#0001
#0002
#0003
#0004
#0005
#0006
#0007
#0008
#0009
TokenStream是一个抽象类,接口只有两个:Next()和Close()。Next()返回当前的token,并指向下一个token;没有token则返回null。
Analyzer也是一个抽象类。默认的TokenStream() (#0005)就是构造并返回一个TokenStream的对象。
#0001
#0002
#0003
#0004
#0005
#0006
#0007
再看它的一个子类WhitespaceTokenizer:
#0001
#0002
#0003
#0004
#0005
#0006
#0007
#0008
#0009
#0010
#0021
#0022
#0023
#0024
#0025
#0026
#0027
#0028
#0029
#0030
#0031
#0032
#0033
#0034
#0035
#0036
#0037
#0038
#0039
#0040
#0041
#0042
#0043
#0044
#0045
#0046
#0047
#0048
#0049
#0050
#0051
#0052
#0053
#0054
#0055
#0056
#0057
#0058
#0059
#0060
#0061
#0062
#0063
#0064
#0065
#0066
#0067
#0068
#0069
#0070
#0071
#0072
#0073
#0074
#0075
#0076
#0077
#0078
#0079
#0080
#0081
#0082
#0083
#0084
#0085
#0086
#0087
#0088
#0089
#0090
#0091
#0092
#0093
#0094
#0095
#0096
#0097
#0098
#0099
几个类之间的关系:WhitespaceTokenizerCharTokenizerTokenizerTokenStream。而 CharTokenizer.Next()是一个关键(#0037~#0083)。它从缓冲区中找到分割符(#0062),然后用识别出来的字符串 (#0070)生成一个Token对象(#0081)。其余的Tokenizer只要定义不同的分割符号集合(#0032)就可以了。例如 WhitespaceTokenizer只要告诉Next()“只要不是white space就是分割符号”就可以了(#0005~#0008)。