5、Analyzer分析器

1.1Analyzer使用時機
1.1.1    索引時使用Analyzer
         輸入關鍵字進行搜索,當需要讓該關鍵字與文檔域內容所包含的詞進行匹配時需要對文檔域內容進行分析,需要經過Analyzer分析器處理生成語彙單元(Token)。分析器分析的對象是文檔中的Field域。當Field的屬性tokenized(是否分詞)爲true時會對Field值進行分析,如下圖:
 

對於一些Field可以不用分析:
1、不作爲查詢條件的內容,比如文件路徑
2、不是匹配內容中的詞而匹配Field的整體內容,比如訂單號、身份證號等。
 
1.1.2    搜索時使用Analyzer
         對搜索關鍵字進行分析和索引分析一樣,使用Analyzer對搜索關鍵字進行分析、分詞處理,使用分析後每個詞語進行搜索。比如:搜索關鍵字:spring web,經過分析器進行分詞,得出:spring  web拿詞去索引詞典表查找,找到索引鏈接到Document,解析Document內容。
         對於匹配整體Field域的查詢可以在搜索時不分析,比如根據訂單號、身份證號查詢等。
 
        注意:搜索使用的分析器要和索引使用的分析器一致。
 
 
1.2Analyzer執行過程
1.2.1    代碼分析
         Analyzer是一個抽象類,在Lucenelucene-analyzers-common包中提供了很多分析器,比如:org.apache.lucene.analysis.standard.standardAnalyzer標準分詞器,它是Lucene的核心分詞器,它對分析文本進行分詞、大寫轉成小寫、去除停用詞、去除標點符號等操作過程。        
 
         什麼是停用詞?停用詞是爲節省存儲空間和提高搜索效率,搜索引擎在索引頁面或處理搜索請求時會自動忽略某些字或詞,這些字或詞即被稱爲Stop Words(停用詞)。比如語氣助詞、副詞、介詞、連接詞等,通常自身並無明確的意義,只有將其放入一個完整的句子中才有一定作用,如常見的“的”、“在”、“是”、“啊”等。
 
如下是org.apache.lucene.analysis.standard.standardAnalyzer的部分源碼: 
final StandardTokenizer src = new StandardTokenizer(getVersion(), reader);//創建分詞器
src.setMaxTokenLength(maxTokenLength);
TokenStream tok = new StandardFilter(getVersion(), src);//創建標準分詞過濾器
tok = new LowerCaseFilter(getVersion(), tok);//在標準分詞過濾器的基礎上加大小寫轉換過濾
tok = new StopFilter(getVersion(), tok, stopwords);//在上邊過濾器基礎上加停用詞過濾 
 
如下圖是語彙單元的生成過程:
 
從一個Reader字符流開始,創建一個基於ReaderTokenizer分詞器,經過三個TokenFilter生成語彙單元Token
 
1.2.2    TokenStream
         TokenStream是語彙單元流,tokenStream是一個抽象類,它是所有分析器的基類,如下圖:

Tokenizer是分詞器,負責將reader轉換爲語彙單元即進行分詞,Lucene提供了很多的分詞器,也可以使用第三方的分詞,比如IKAnalyzer一箇中文分詞器。 
tokenFilter是分詞過濾器,負責對語彙單元進行過濾,tokenFilter可以是一個過濾器鏈兒,Lucene提供了很多的分詞器過濾器,比如大小寫轉換、去除停用詞等。 
從TokenStream中獲取語彙單元信息,如下代碼:
//創建分析器
Analyzer analyzer = new StandardAnalyzer();
//得到TokenStream
TokenStream tokenStream = analyzer.tokenStream("content", new StringReader("Lucene is a Java full-text search engine"));
//設置tokenStream初始狀態,否則會拋異常
tokenStream.reset();
//設置要獲取分詞的偏移量
OffsetAttribute offsetAttribute = tokenStream.addAttribute(OffsetAttribute.class);
//設置要獲取分詞的項
CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
 while(tokenStream.incrementToken()){
   System.out.println("-----------------");
   //起始偏移量
   System.out.print("-->"+offsetAttribute.startOffset());
   //截止偏移量
   System.out.print("-->"+offsetAttribute.endOffset());
   //分詞項的值
  System.out.println("-->"+new String(charTermAttribute.toString()));          
 } 
1.3中文分詞器
1.3.1    什麼是中文分詞器
         學過英文的都知道,英文是以單詞爲單位的,單詞與單詞之間以空格或者逗號句號隔開。而中文則以字爲單位,字又組成詞,字和詞再組成句子。所以對於英文,我們可以簡單以空格判斷某個字符串是否爲一個單詞,比如I love Chinalove China很容易被程序區分開來;但中文“我愛中國”就不一樣了,電腦不知道“中國”是一個詞語還是“愛中”是一個詞語。把中文的句子切分成有意義的詞,就是中文分詞,也稱切詞。我愛中國,分詞的結果是:我 中國。
 
1.3.2    Lucene自帶中文分詞器
l StandardAnalyzer
單字分詞:就是按照中文一個字一個字地進行分詞。如:我愛中國
效果:
l CJKAnalyzer
二分法分詞:按兩個字進行切分。如:我是中國人,效果:我是是中中國“國人”。
 
上邊兩個分詞器無法滿足需求。
l SmartChineseAnalyzer
對中文支持較好,但擴展性差,擴展詞庫,禁用詞庫和同義詞庫等不好處理
 
1.3.3    第三方產品介紹
· paoding: 庖丁解牛最新版在 https://code.google.com/p/paoding/ 中最多支持Lucene 3.0,且最新提交的代碼在 2008-06-03,在svn中最新也是2010年提交,已經過時,不予考慮。
· mmseg4j:最新版已從 https://code.google.com/p/mmseg4j/ 移至 https://github.com/chenlb/mmseg4j-solr,支持Lucene 4.10,且在github中最新提交代碼是2014年6月,從09年~14年一共有:18個版本,也就是一年幾乎有3個大小版本,有較大的活躍度,用了mmseg算法。
· IK-analyzer: 最新版在https://code.google.com/p/ik-analyzer/上,支持Lucene 4.10從2006年12月推出1.0版開始, IKAnalyzer已經推出了4個大版本。最初,它是以開源項目Luence爲應用主體的,結合詞典分詞和文法分析算法的中文分詞組件。從3.0版本開 始,IK發展爲面向Java的公用分詞組件,獨立於Lucene項目,同時提供了對Lucene的默認優化實現。在2012版本中,IK實現了簡單的分詞 歧義排除算法,標誌着IK分詞器從單純的詞典分詞向模擬語義分詞衍化。 但是也就是2012年12月後沒有在更新。 這是我們用到的版本
· ansj_seg:最新版本在 https://github.com/NLPchina/ansj_seg tags僅有1.1版本,從2012年到2014年更新了大小6次,但是作者本人在2014年10月10日說明:“可能我以後沒有精力來維護ansj_seg了”,現在由”nlp_china”管理。2014年11月有更新。並未說明是否支持Lucene,是一個由CRF(條件隨機場)算法所做的分詞算法。
· imdict-chinese-analyzer:最新版在 https://code.google.com/p/imdict-chinese-analyzer/ , 最新更新也在2009年5月,下載源碼,不支持Lucene 4.10 。是利用HMM(隱馬爾科夫鏈)算法。
· Jcseg:最新版本在git.oschina.net/lionsoul/jcseg,支持Lucene 4.10,作者有較高的活躍度。利用mmseg算法。
 

IKAnalyzer的介紹
        IKAnalyzer是一個開源的,基於Java語言開發的輕量級的中文分詞語言包,它是以Lucene爲應用主體,結合詞典分詞和文法分析算法的中文詞組組件。 從3.0版本開始,IK發展爲面向java的公用分詞組件,獨立Lucene項目,同時提供了對Lucene的默認優化實現。 IKAnalyzer實現了簡單的分詞歧義排除算法,標誌着IK分詞器從單獨的詞典分詞想模擬語義化分詞衍生。
        IKAnalyzer 的新特性:
        1、.採用了特有的“正向迭代最細粒度切分算法“,支持細粒度和智能分詞兩種切分模式;  
        2、在系統環境:Core2 i7 3.4G雙核,4G內存,window 7 64位, Sun JDK 1.6_29 64位 普通pc環境測試,IK2012具有160萬字/秒(3000KB/S)的高速處理能力。 
        3、2012版本的智能分詞模式支持簡單的分詞排歧義處理和數量詞合併輸出。  
        4、採用了多子處理器分析模式,支持:英文字母、數字、中文詞彙等分詞處理,兼容韓文、日文字符  
        5、優化的詞典存儲,更小的內存佔用。支持用戶詞典擴展定義。特別的,在2012版本,詞典支持中文,英文,數字混合詞語。

第一步:把IKAnalyzer2012FF_u1.jar添加到工程中 或通過pom.xml 的方式引用
<dependency>
    <groupId>com.janeluo</groupId>
    <artifactId>ikanalyzer</artifactId>
    <version>2012_u6</version>
</dependency>
第二步:把配置文件和擴展詞典、停用詞詞典添加到工程的classpath下。
注意:擴展詞典和停用詞詞典要保證字符集utf-8嚴禁使用記事本編輯,如果無意使記事本的方式,進行了保存,或者通過IDE進行了保存,沒關係,只要在dic 開頭加一空行,也是可以解決這個問題的
普通項目直接放在src下即可

maven項目 放在resources 下即可

測試代碼如下: 4.10.3版本的寫法是
	//查看分析器的分析效果
	@Test
	public void testAnalyzer() throws Exception {
		//創建一分析器對象
//		Analyzer analyzer = new StandardAnalyzer(); //標準
//		Analyzer analyzer = new CJKAnalyzer();
//		Analyzer analyzer = new SmartChineseAnalyzer();
		Analyzer analyzer = new IKAnalyzer();  //使用最多的中文分析器
		//獲得tokenStream對象
		//第一個參數是域的名稱,可以給一個任意值
		//第二個參數:要分析的文本內容
		TokenStream tokenStream = analyzer.tokenStream("test", "闊小括高富帥Lucene是");
		CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
		//調用reset方法
		tokenStream.reset();
		//遍歷關鍵詞列表
		while (tokenStream.incrementToken()) {
			System.out.println(charTermAttribute.toString());
		}
		//關閉tokenstream
		tokenStream.close();
	}
1.3.5    自定義詞庫
classpath下定義IKAnalyzer.cfg.xml文件,如下:
<?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">mydict.dic</entry>
     <!-- 用戶可以在這裏配置自己的擴展停用詞字典    -->
    <entry key="ext_stopwords">ext_stopword.dic</entry>
 
</properties>
classpath下的編輯mydict.dic文件,此文件中存儲擴展詞庫,在ext_stopword.dic文件中存放停用詞。
注意:mydict.dicext_stopword.dic文件的格式爲UTF-8,注意是無BOM UTF-8 編碼。 
使用EditPlus.exe保存爲無BOM UTF-8 編碼格式,如下圖:

 測試代碼如下: 6.3版本的寫法相對要複雜一些 要分爲以下幾個類來實現。
第一步:自定義MyWordAnalyzer 類 繼承Analyzer抽象類,並重寫createComponents方法
內容如下:
@Override
protected TokenStreamComponents createComponents(String s) {
    Reader reader=null;
    try{
        reader=new StringReader(s);
        MyWordTokenizer it = new MyWordTokenizer(reader);
        return new Analyzer.TokenStreamComponents(it);
    }finally {
        IOUtils.closeWhileHandlingException(reader);
    }
第二步:自定義MyWordTokenizer類繼承Tokenizer抽象類 並重寫其實中方法
內容如下:
import org.apache.lucene.analysis.Tokenizer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
import org.apache.lucene.analysis.tokenattributes.TypeAttribute;
import org.wltea.analyzer.core.IKSegmenter;
import org.wltea.analyzer.core.Lexeme;

import java.io.IOException;
import java.io.Reader;

public class MyWordTokenizer extends Tokenizer {
    // IK分詞器實現
    private IKSegmenter _IKImplement;
    // 詞元文本屬性
    private final CharTermAttribute termAtt;
    // 詞元位移屬性
    private final OffsetAttribute offsetAtt;
    // 詞元分類屬性(該屬性分類參考org.wltea.analyzer.core.Lexeme中的分類常量)
    private final TypeAttribute typeAtt;
    // 記錄最後一個詞元的結束位置
    private int endPosition;
    public MyWordTokenizer(Reader in) {
        this(in, true);
    }
    public MyWordTokenizer(Reader in, boolean useSmart) {
        offsetAtt = addAttribute(OffsetAttribute.class);
        termAtt = addAttribute(CharTermAttribute.class);
        typeAtt = addAttribute(TypeAttribute.class);
        _IKImplement = new IKSegmenter(input, useSmart);
    }
    @Override
    public final boolean incrementToken() throws IOException {
        // 清除所有的詞元屬性
        clearAttributes();
        Lexeme nextLexeme = _IKImplement.next();
        if (nextLexeme != null) {
            // 將Lexeme轉成Attributes
            // 設置詞元文本
            termAtt.append(nextLexeme.getLexemeText());
            // 設置詞元長度
            termAtt.setLength(nextLexeme.getLength());
            // 設置詞元位移
            offsetAtt.setOffset(nextLexeme.getBeginPosition(),
                    nextLexeme.getEndPosition());
            // 記錄分詞的最後位置
            endPosition = nextLexeme.getEndPosition();
            // 記錄詞元分類
            typeAtt.setType(nextLexeme.getLexemeTypeString());
            // 返會true告知還有下個詞元
            return true;
        }
        // 返會false告知詞元輸出完畢
        return false;
    }
    public void reset() throws IOException {
        super.reset();
        _IKImplement.reset(input);
    }
    @Override
    public final void end() {
        // set final offset
        int finalOffset = correctOffset(this.endPosition);
        offsetAtt.setOffset(finalOffset, finalOffset);
    }
}
測試代碼如下
@Test
public  void  createIKAnalyzer() throws Exception {
    //中文分析器 IKAnalyzer
    //Analyzer analyzer = new IKAnalyzer();
    //獲得tokenStream對象
    //第一個參數是域的名稱,可以給一個任意值
    //第二個參數:要分析的文本內容
    //TokenStream tokenStream = analyzer.tokenStream("createIKAnalyzer", "Lucene是apache軟件基金會4 jakarta項目組的一個子項目,是一個開放源代碼的全文檢索引擎工具包,即它不是一個完整的全文檢索引擎,而是一個全文檢索引擎的架構");
    Analyzer analyzer=new MyWordAnalyzer();
    TokenStream tokenStream = analyzer.tokenStream("f", "傳智播客白富美也然並卵Lucene是apache軟件基金會4 jakarta項目組的一個子項目,是一個開放源代碼的全文檢索引擎工具包,即它不是一個完整的全文檢索引擎,而是一個全文檢索引擎的架構");
    //設置一個關鍵詞引用
    CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
    //調用reset方法
    tokenStream.reset();
    //遍歷關鍵詞列表
    while (tokenStream.incrementToken()) {
        System.out.println(charTermAttribute.toString());
    }
    //關閉tokenstream
    tokenStream.close();
}


下面是小編的微信轉帳二維碼,小編再次謝謝讀者的支持,小編會更努力的

----請看下方↓↓↓↓↓↓↓

百度搜索 Drools從入門到精通:可下載開源全套Drools教程

深度Drools教程不段更新中:


更多Drools實戰陸續發佈中………

掃描下方二維碼關注公衆號 ↓↓↓↓↓↓↓↓↓↓







發佈了96 篇原創文章 · 獲贊 100 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章