【转载】Lucene学习笔记(五)

来源:互联网 发布:灰色预测模型软件 编辑:程序博客网 时间:2024/06/01 10:03
五、Lucene的分析器:
大纲:1. Lucene分析器Analyzer
2. JavaCC与Tokenizer
3. Lucene内建的分析器
4. 定制自己的分词器和过滤器

1. Lucene分析器----Analyzer

1.1 Analyzer的概述:中文翻译是"分析器"。
它主要用于分析切词或者过滤搜索时遇到的各种文本。用更具体的话说其实就是"分词"和"过滤"。

** 作用:分析器作用与索引和文本之间,索引要进入索引库的文本资源都要先通过分析器的分析(切词和过滤),
以便确保索引库中的索引内容的正确性。
如果不经过分析器分析就把文本内容存入索引中,那么会引起索引内容数据的不一致,也会降低索引的效率,
进而影响整个搜索引擎的性能。

** 注意:一个分析器主要包括分词器和过滤器两种组件。

1.1.1 分词器:就是用于对文本资源进行切词,
将文本按照规则切分为一个个可以进入索引的最小单位--分词。(具体下边标题会讲到)

1.1.2 过滤器:对分词器切成的索引最小单位,即对分词进行预处理操作。
比如大写转小写、复数转单数、根据语义改写拼写错误的单词等。(具体下边标题会讲到)

** 说明:此处我们着重讲解Analyzer分析器的实现。所有的分析器都是继承的org.apache.lucene.analysis.Analyzer抽象类,
该类中只定义了两个方法《自己可查看源代码》

1.2 分词器(Tokenizer)和过滤器(TokenFilter):前边说过分析器的功能就是进行分词和过滤。
所以在Analyzer分析器的对象中一定会保存对分词器对像和过滤器对象的引用,然后调用分词器对象和过滤器对象的API接口,
以便完成切词和过滤的功能目的。

** 说明:在Lucene中,分词器和过滤器分别是通过继承抽象类Tokenizer和TokenFilter来实现的。
《可以自己查看Tokenizer和TokenFilter类的源代码》

** 注意:从分析器的功能角度来看,其本身并不能做任何事,因为它完全依赖与分词器和过滤器完成目的任务。
一个分析器的所有工作就是将分词器和过滤器进行合理的组合,利用它们对文本完成分词和过滤的效果。

1.3 使用StandardAnalyzer进行测试:上一小节中了解到,要使用Lucene的分析机制,就要扩展Lucene的分析器基类。
Lucene为我们提供了一个实现Analyzer的相当标准的分析器----StandardAnalyzer类。

** 说明:StandardAnalyzer可以理解成"标准分析器",它是最容易使用也是使用最频繁的一种Analyzer,
它内部使用了Lucene自带的几种分词器和过滤器,来完成该标准分析器的功能效果。

例子:
//创建一个测试类
public class StandardAnalyzerTest{
//构造方法
public StandardAnalyzerTest(){}

//入口主方法
public static void main(String[] args){
//初始化一个StandardAnalyzer对象
Analyzer analyzer = new StandardAnalyzer();
//创建进行分析测试的字符串数据信息
StringReader reader = new StringReader("People are always talking about 'the problem of youth'.");

//创建一个TokenStream对象
TokenStream tokenStream = analyzer.tokenStream(reader);
//作为辅助输出时显示的行号
int i = 0;
//调用TokenStream类next()方法得到分析器分词过滤后的封装有分词信息的Token对象
Token token = tokenSream.next();
while(token != null){
//增加i行号值
i++;
//Token.termText()是分析器分词过滤后的分词内容
System.out.println("行数 " + i + " : " + token.termText());
//取得下一个Token分词,然后再次输出
token = tokenStream.next();
}
}
}
** 结果:
调用Analyzer.tokenStream(reader);意思就是将待分析的流对象,放入到分析器中进行一些准备信息,但是并没有分词和过滤,
完成之后返回一个Lucene提供的TokenStream类的对象
(** 注意:此时的TokenStream中保存的信息和传入的reader中保存的数据完全一样,就是说此时并没有完成分词和过滤)
调用TokenStream类next()方法得到分析器分词过滤后的封装有分词信息的Token对象
(** 注意:调用next方法的时候会对TokenStream中的要返回的数据
(而不是所有的TokenStream中的数据都一次性的分词、过滤)
进行分词和过滤),
再调用Token.termText()就拿到了分词内容。
打印结果:
行1:people
行2:always
行3:talking
行4:about
行5:problem
行6:youth

** StandardAnalyzer分析器的效果:

1.3.1 对原有句子按照空格进行了分词(分词器干的)
1.3.2 所有的大写字母都转换成了小写字母(过滤器干的)
1.3.3 除掉了"are"、"of"、"the"等单词(过滤器干的)
1.3.4 删除了所有的标点符号(过滤器干的)

** StandardAnalyzer分析器对中文的处理:将会把中文文本,一个字一个字的进行分词,也表明该分析器对中文的支持不太好。

例如:代码不变,将输入文本改成:"我是中国人"

打印结果:
行1:我
行2:是
行3:中
行4:国
行5:人
** 说明:结果说明StandardAnalyzer分析器对于中文只能按照单字进行切词,除了切词还过滤掉了标点符号。
如果实际中使用该分析器对中文创建索引,那么带来的缺点是使索引的内容过于庞大等,不便于建立索引和搜索效率的提高。
《中文分析器后边会讲》

2. JavaCC与Tokenizer :具体可以再次查看《Lucene指导书籍》,因为这部分没什么用处,完全可以不看。
2.1 JavaCC简介:不重要。
2.2 通过JavaCC构建的Lucene标准分析器原理:不重要。

3. Lucene内建的分析器:Lucene提供了几种不同环境和需求下使用的Analyzer分析器,
最常用的有StandardAnalyzer、SimpleAnalyzer和WhitespaceAnalyzer等。

3.1 标准分析器----StandardAnalyzer类:在这我们说一下它的实现原理,看看它究竟是什么时机使用分词器分词和过滤器过滤的。

3.1.1 由前边代码可知调用了StandardAnalyzer的tokenStream方法,看起源码后知道:
将传入的Reader对象作为参数,
分别使用了StandardTokenizer、StandardFilter、LowerCaseFilter、StopFilter的构造方法构造了对应的对象,
但是构造方法中都什么都没座,也就是说"标准分析器"StandardAnalyzer的分词和过滤不是通过调用tokenStream方法实现的。

3.1.2 测试中除了tokenStream方法外就是调用的TokenStream的next()方法,所以也可以猜到,分析器的分词和过滤就是通过该方法实现的。
调用next时
(这是针对我们上边的测试而言,如果是真正我们项目中用Lucene时,因为是创建索引和对搜索关键字处理时会用到分析器
所以想看到调用tokenStream方法和next方法只能到创建索引和对搜索关键字进行处理的代码中找到
如IndexWriter.addDocument()中调用DocumentWriter的方法中就能找到类似上边测试的代码),
因为最后一个构造的是StopFilter类的对象,所以调用的是StopFilter类的next方法,跟踪源码发现,
该next方法中有input.next(),其实input是作为参数传入的对象,弄明白后发现调用的是LowerCaseFilter的next方法,
一次这样跟踪最后发现将会最先调用StandardTokenizer类的next方法,她是从Reader中取出下一个词,然后进行分词和过滤。

例子:可以参看上边例子自己写一个简单的测试,这里就不列举了,因为比较简单。

3.2 通用词分析器----StopAnalyzer类:
该类的tokenStream方法return new StopFilter(new LowerCaseTokenizer(reader), stopWords);
很明显它在实现了基本的分词和小写转换的基础上,还会将一些"停用词"从文档数据中删除。

** 注意:在StopAnalyzer类中定义了一个默认的停用词列表(数组),如果我们没有指定自己定义的停用词列表,
那么运行的时候就会使用这个默认的停用词列表。
这个默认停用词表是针对英文定义的。
public static final String[] ENGLISH_STOP_WORDS = {
"a", "an", "and", "are", "as", "at", "be", "but", "by",
"for", "if", "in", "into", "is", "it",
"no", "not", "of", "on", "or", "s", "such",
"t", "that", "the", "their", "then", "there", "these",
"they", "this", "to", "was", "will", "with"
};

** 使用:如何使用我们自己定义的停用词呢?使用StopAnalyzer的构造方法传入定义好的停用词列表进行构建分析器对象即可。

例子:
//创建一个测试类
public class StandardAnalyzerTest{
//构造方法
public StandardAnalyzerTest(){}

//入口主方法
public static void main(String[] args){
//创建停用词
String[] stopWords = {"always"};
//初始化一个StandardAnalyzer对象
Analyzer analyzer = new StopAnalyzer(stopWords);
//创建进行分析测试的字符串数据信息
StringReader reader = new StringReader("People are always talking about 'the problem of youth'.");

//创建一个TokenStream对象
TokenStream tokenStream = analyzer.tokenStream(reader);
//作为辅助输出时显示的行号
int i = 0;
//调用TokenStream类next()方法得到分析器分词过滤后的封装有分词信息的Token对象
Token token = tokenSream.next();
while(token != null){
//增加i行号值
i++;
//Token.termText()是分析器分词过滤后的分词内容
System.out.println("行数 " + i + " : " + token.termText());
//取得下一个Token分词,然后再次输出
token = tokenStream.next();
}
}
}
** 结果:将会看到只有always这个单词被删除了,即停用。
上边构造StopAnalyzer的时候如果使用默认的构造方法,那么使用的将会是默认的停用词列表。

** 问题:能够移除停用词非常有益,不过同时也带来了另一个有趣的问题:就是移除所谓的停用词之后,所留下来的空间如何处理??

** 分析:这个问题就是会带来语义查询时可能会造成返回的结果不太准确,不过影响不大,如果感兴趣可以自研究。

3.3 空格分析器----WhitespaceAnalyzer类:用于对空格进行切分的一个分析器。就是按照空格进行切词。
它的tokenStream方法return new WhitespaceTokenizer(reader);就是只使用了空格分词器,而没有使用任何过滤器。

例子:代码基本同上,修改一下代码:
//初始化一个StandardAnalyzer对象
Analyzer analyzer = new WhitespaceAnalyzer();
//创建进行分析测试的字符串数据信息
StringReader reader = new StringReader("Today is sunday,saturday and monday.");

** 测试结果为:
Today
is
sunday,aturday
and
monday.

** 可以看出来只是按照空格分词,标点不好没有去掉,停用词也没有去掉。

3.4 其他分析器:Lucene还提供了不少的分析器,例如SimpleAnalyzer、GermanAnalyzer等,这些就不一一列举了,
感兴趣的自己学习。

原创粉丝点击