一、概述
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