Lucene-分詞器API
1. org.apache.lucene.analysi.Analyzer
分析器,分詞器組件的核心API,它的職責:構建真正對文本進行分詞處理的TokenStream(分詞處理器)。通過調用它的如下兩個方法,得到輸入文本的分詞處理器。
public final TokenStream tokenStream(String fieldName, Reader reader)
public final TokenStream tokenStream(String fieldName, String text)
2.TokenStreamComponents createComponents(String fieldName)
Analizer中唯一的抽象方法,擴展點。通過提供該方法的實現來實現自己的Analyzer。
返回值爲 TokenStreamComponents 分詞處理器組件。
參數說明:fieldName,如果我們需要爲不同的字段創建不同的分詞處理器組件,則可根據這個參數來判斷。否則,就用不到這個參數。
3.TokenStreamComponents
分詞處理器組件:這個類中封裝有供外部使用的TokenStream分詞處理器。提供了對source(源)和sink(供外部使用分詞處理器)兩個屬性的訪問方法。
4.org.apache.lucene.analysis.TokenStream
分詞器處理項,負責對輸入文本完成分詞、處理。
TokenStream 的兩類子類
Tokenizer:分詞器,輸入是Reader字符流的TokenStream,完成從流中分出分項
TokenFilter:分項過濾器,它的輸入是另一個TokenStream,完成對從上一個TokenStream中流出的token的特殊處理。
TokenStream 繼承了 AttributeSource
Attribute 屬性 ,TokenAttribute 分項屬性(分項信息),如 分項的詞、詞的索引位置等等。這些屬性通過不同的Tokenizer /TokenFilter處理統計得出。不同的Tokenizer/TokenFilter組合,就會有不同的分項信息。
5.AttributeSource使用規則說明
1、某個TokenStream實現中如要存儲分項屬性,通過AttributeSource的兩個add方法之一,往AttributeSource中加入屬性對象。
<T extends Attribute> T addAttribute(Class<T> attClass)
該方法要求傳人你需要添加的屬性的接口類(繼承Attribute),返回對應的實現類實例給你。從接口到實例,這就是爲什麼需要AttributeFactory的原因。
void addAttributeImpl(AttributeImpl att)
2、加入的每一個Attribute實現類在AttributeSource中只會有一個實例,分詞過程中,分項是重複使用這一實例來存放分項的屬性信息。重複調用add方法添加它返回已存儲的實例對象。
3、要獲取分項的某屬性信息,則需持有某屬性的實例對象,通過addAttribute方法或getAttribure方法獲得Attribute對象,再調用實例的方法來獲取、設置值
4、在TokenStream中,我們用自己實現的Attribute,默認的工廠。當我們調用這個add方法時,它怎麼知道實現類是哪個?這裏有一定規則要遵守:
1)、自定義的屬性接口 MyAttribute 繼承 Attribute
2)、自定義的屬性實現類必須繼承 Attribute,實現自定義的接口MyAttribute
3)、自定義的屬性實現類必須提供無參構造方法
4)、爲了讓默認工廠能根據自定義接口找到實現類,實現類名需爲 接口名+Impl 。
6.TokenStream 的使用步驟。
1、從tokenStream獲得你想要獲得分項屬性對象(信息是存放在屬性對象中的)
2、調用 tokenStream 的 reset() 方法,進行重置。因爲tokenStream是重複利用的。
3、循環調用tokenStream的incrementToken(),一個一個分詞,直到它返回false
4、在循環中取出每個分項你想要的屬性值。
5、調用tokenStream的end(),執行任務需要的結束處理。
6、調用tokenStream的close()方法,釋放佔有的資源。
7.Lucene提供的分詞器
1)<!-- Lucene提供的中文分詞器模塊,lucene-analyzers-smartcn -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-smartcn</artifactId>
<version>7.3.0</version>
</dependency>
2)IKAnalyzer
開源、輕量級的中文分詞器,應用比較多,最先是作爲lucene上使用而開發,後來發展爲獨立的分詞組件。
只提供到Lucene 4.0版本的支持。我們在4.0以後版本Lucene中使用就需要簡單集成一下。
<!-- ikanalyzer 中文分詞器 -->
<dependency>
<groupId>com.janeluo</groupId>
<artifactId>ikanalyzer</artifactId>
<version>2012_u6</version>
<exclusions>
<exclusion>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- lucene-queryparser 查詢分析器模塊 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>7.3.0</version>
</dependency>
8.擴展 IKAnalyzer的停用詞
Ik中默認的停用詞很少,可擴展它。可從網址: https://github.com/cseryp/stopwords,下載一份比較全的停用詞。
Ik中停用詞的擴展步驟:
1、在類目錄下創建IK的配置文件:IKAnalyzer.cfg.xml
2、在配置文件中增加配置擴展停用詞文件的節點:
<entry key=“ext_stopwords”>my_ext_stopword.dic</entry>
如有多個,以“;”間隔
3、在類目錄下創建我們的擴展停用詞文件 my_ext_stopword.dic
4、編輯該文件加入停用詞,一行一個
注:文件編碼一定是UTF-8
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 擴展配置</comment>
<!--在這裏配置自己的擴展字典 -->
<entry key="ext_dict">ext.dic</entry>
<!--在這裏配置自己的擴展停止詞字典-->
<entry key="ext_stopwords">my_ext_stopword.dic</entry>
</properties>
自定義分分詞器實現
package com.dongsheng.lucene.dslucene.lucene;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenFilter;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.Tokenizer;
import org.apache.lucene.util.Attribute;
import org.apache.lucene.util.AttributeImpl;
import org.apache.lucene.util.AttributeReflector;
import java.io.IOException;
/***
*@author dongsheng
*@date 2020/2/19 16:47
*@version 1.0.0
*@Description
*/
public class MyWhitespaceAnalyzer extends Analyzer {
@Override
protected TokenStreamComponents createComponents(String fieldName) {
Tokenizer source = new MyWhitespaceTokenizer();
TokenStream filter = new MyLowerCaseTokenFilter(source);
return new TokenStreamComponents(source, filter);
}
//自定義分詞器讀取
static class MyWhitespaceTokenizer extends Tokenizer {
// 需要記錄的屬性
// 詞
MyCharAttribute charAttr = this.addAttribute(MyCharAttribute.class);
char[] buffer = new char[255];
int length = 0;
int c;
@Override
public boolean incrementToken() throws IOException {
// 清除所有的詞元屬性
clearAttributes();
length = 0;
while (true) {
c = this.input.read();
if (c == -1) {
if (length > 0) {
// 複製到charAttr
this.charAttr.setChars(buffer, length);
return true;
} else {
return false;
}
}
if (Character.isWhitespace(c)) {
if (length > 0) {
// 複製到charAttr
this.charAttr.setChars(buffer, length);
return true;
}
}
buffer[length++] = (char) c;
}
}
}
//自定義分詞項
public static class MyLowerCaseTokenFilter extends TokenFilter {
public MyLowerCaseTokenFilter(TokenStream input) {
super(input);
}
MyCharAttribute charAttr = this.addAttribute(MyCharAttribute.class);
@Override
public boolean incrementToken() throws IOException {
boolean res = this.input.incrementToken();
if (res) {
char[] chars = charAttr.getChars();
int length = charAttr.getLength();
if (length > 0) {
for (int i = 0; i < length; i++) {
chars[i] = Character.toLowerCase(chars[i]);
}
}
}
return res;
}
}
public static interface MyCharAttribute extends Attribute {
void setChars(char[] buffer, int length);
char[] getChars();
int getLength();
String getString();
}
public static class MyCharAttributeImpl extends AttributeImpl
implements MyCharAttribute {
private char[] chatTerm = new char[255];
private int length = 0;
@Override
public void setChars(char[] buffer, int length) {
this.length = length;
if (length > 0) {
System.arraycopy(buffer, 0, this.chatTerm, 0, length);
}
}
public char[] getChars() {
return this.chatTerm;
}
public int getLength() {
return this.length;
}
@Override
public String getString() {
if (this.length > 0) {
return new String(this.chatTerm, 0, length);
}
return null;
}
@Override
public void clear() {
this.length = 0;
}
@Override
public void reflectWith(AttributeReflector reflector) {
}
@Override
public void copyTo(AttributeImpl target) {
}
}
public static void main(String[] args) {
//中間空格的代替|
String text = "An AttributeSource contains a list of different AttributeImpls, and methods to add and get them. ";
try (Analyzer ana = new MyWhitespaceAnalyzer();
TokenStream ts = ana.tokenStream("test", text);) {
MyCharAttribute ca = ts.getAttribute(MyCharAttribute.class);
ts.reset();
while (ts.incrementToken()) {
System.out.print(ca.getString() + "|");
}
ts.end();
System.out.println();
} catch (IOException e) {
e.printStackTrace();
}
}
}
結果輸出:
an|attributesource|contains|a|list|of|different|attributeimpls,|and|methods|to|add|and|get|them.|