一、概述
1.1 分词的基本过程
首先是TokenStream
通过接收一个StringReader
流将需要进行分词的内容读入进来,TokenStream
有两个子抽象类Tokenizer
和TokenFilter
。读入的过程为:StringReader
流经过Tokenizer
接收输入流并根据输入流进行词切分,然后会经过多个TokenFilter
对TokenStream
进行过滤,例如去掉一些索引词、替代同义索引词等操作,最后生成TokenStream
。
1.2 Tokenizer
1
Tokenizer
主要负责接收Reader
字节流,将Reader
进行分词操作,即将一组数据划分不同的语汇单元。KeyWordTokenizer
是关键词分词,StandardTokenizer
是标准分词,CharTokenizer
是字符分词,WhitespaceTokenizer
是空白分词,LetterTokenizer
是标点分词,LowerCaseTokenizer
是小写分词(将各个语汇单元转换成小写)。这里我们说明一下
WhitespaceTokenizer
和LetterTokenizer
,比如有这样一句内容:how are you I’m a teacher
。那么前者会分成这样:how、 are、 you、 I’m、 a、 teacher
,而后者会分成:how、 are、 you、 I 、m、 a、 teacher
。
1.3 TokenFilter
2
TokenFilter
类继承于TokenStream
,其输入是另一个TokenStream
,主要职责是对TokenStream
进行过滤,例如去掉一些索引词、替代同义索引词等操作。上面给出各类过滤器,这里只是作为了解,后面再细说。
二、入门示例(工程lucene_analyzer01
)
AnalyzerUtils.java
package cn.itcast.util;
import java.io.IOException;
import java.io.StringReader;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
import org.apache.lucene.analysis.tokenattributes.TypeAttribute;
public class AnalyzerUtils {
public static void displayToken(String str, Analyzer analyzer) {
try {
//首先我们使用分词器analyzer将相关数据(这里比如是内容gcontent)进行分词,这样得到一个词汇流
//然后我们给这个流做一个标记,可以用来遍历此流
TokenStream stream = analyzer.tokenStream("content",new StringReader(str));//这就是一个词汇流
CharTermAttribute cta = stream.addAttribute(CharTermAttribute.class);//相当于一个标记,随着流增加
while (stream.incrementToken()) {
System.out.print("[" + cta + "]");
}
System.out.println();
} catch (IOException e) {
e.printStackTrace();
}
}
}
测试:TestAnalyzer.java
@Test
public void test01(){
Analyzer analyzer1 = new StandardAnalyzer(Version.LUCENE_35);//标准分词器
Analyzer analyzer2 = new StopAnalyzer(Version.LUCENE_35);//停用分词器
Analyzer analyzer3 = new SimpleAnalyzer(Version.LUCENE_35);//简单分词器
Analyzer analyzer4 = new WhitespaceAnalyzer(Version.LUCENE_35);//空格分词器
String text = "this is my house,I am come form Xian,My email is "
+ "xxx@qq.com,and my qq is 154625554";
AnalyzerUtils.displayToken(text, analyzer1);
AnalyzerUtils.displayToken(text, analyzer2);
AnalyzerUtils.displayToken(text, analyzer3);
AnalyzerUtils.displayToken(text, analyzer4);
}
说明:可以看到我们构造一个TokenStream
流,此流接收两个参数,第一个参数表示要进行分词的域(这里随便),而第二个参数就是一个StringReader
流。而CharTermAttribute
相当于流中的一个标记,随着流而增减,用户我们遍历流中各个语汇单元。而相关的分词器我们通过参数传入进去。分词结果为:
3
从结果我们可以看到各个分词器的作用和区别。下面我们再测试一下中文分词:
@Test
public void test02(){
//对中文分词不适用
Analyzer analyzer1 = new StandardAnalyzer(Version.LUCENE_35);//标准分词器
Analyzer analyzer2 = new StopAnalyzer(Version.LUCENE_35);//停用分词器
Analyzer analyzer3 = new SimpleAnalyzer(Version.LUCENE_35);//简单分词器
Analyzer analyzer4 = new WhitespaceAnalyzer(Version.LUCENE_35);//空格分词器
String text = "西安市雁塔区";
AnalyzerUtils.displayToken(text, analyzer1);
AnalyzerUtils.displayToken(text, analyzer2);
AnalyzerUtils.displayToken(text, analyzer3);
AnalyzerUtils.displayToken(text, analyzer4);
}
测试结果为:
4
可见一般的分词器对中文分词作用不大。
下面看语汇单元的一些属性:
AnalyzerUtils.java
public static void displayAllTokenInfo(String str, Analyzer analyzer){
try {
TokenStream stream = analyzer.tokenStream("content", new StringReader(str));
PositionIncrementAttribute pia = stream.addAttribute(PositionIncrementAttribute.class);
OffsetAttribute oa = stream.addAttribute(OffsetAttribute.class);
CharTermAttribute cta = stream.addAttribute(CharTermAttribute.class);
TypeAttribute ta = stream.addAttribute(TypeAttribute.class);
while (stream.incrementToken()) {
System.out.print("位置增量: " + pia.getPositionIncrement());//词与词之间的空格
System.out.print(",单词: " + cta + "[" + oa.startOffset() + "," + oa.endOffset() + "]");
System.out.print(",类型: " + ta.type()) ;
System.out.println();
}
System.out.println();
} catch (IOException e) {
e.printStackTrace();
}
}
测试
@Test
public void test03(){
//对中文分词不适用
Analyzer analyzer1 = new StandardAnalyzer(Version.LUCENE_35);//标准分词器
Analyzer analyzer2 = new StopAnalyzer(Version.LUCENE_35);//停用分词器
Analyzer analyzer3 = new SimpleAnalyzer(Version.LUCENE_35);//简单分词器
Analyzer analyzer4 = new WhitespaceAnalyzer(Version.LUCENE_35);//空格分词器
String text = "how are you thank you";
System.out.println("************标准分词器***************");
AnalyzerUtils.displayAllTokenInfo(text, analyzer1);
System.out.println("************停用分词器***************");
AnalyzerUtils.displayAllTokenInfo(text, analyzer2);
System.out.println("************简单分词器***************");
AnalyzerUtils.displayAllTokenInfo(text, analyzer3);
System.out.println("*************空格分词器**************");
AnalyzerUtils.displayAllTokenInfo(text, analyzer4);
}
说明:
- 语汇单元类型
TypeAttribute
:语汇单元的类型,一般有word
和ALPHANUM
两种类型。 - 语汇单元偏移量
OffsetAttribute
:就是起始字符和终止字符之间的偏移量。这里我们举例说明,比如对how are you thank you
进行分词,那么各个语汇单元之间的偏移量为
5 - 语汇单元位置增量
PositionIncrementAttribute
:位置增量就是语汇单元之间的距离,比如上面的分词如果不将某些语汇滤除,那么各个语汇单元之间的位置增量都是1,但是如果有些单元被滤掉,比如are
关键词,那么how
和you
之间的位置增量则为2。我们看测试结果为:
6