【lucene系列學習四】使用IKAnalyzer分詞器實現敏感詞和停用詞過濾


Lucene自帶的中文分詞器SmartChineseAnalyzer不太好擴展,於是我用了IKAnalyzer來進行敏感詞和停用詞的過濾。

首先,下載IKAnalyzer,我下載了

然后,由於IKAnalyzer已經很久不更新了,不兼容現在的Lucene6版本,所以我參考網上的資料,重寫了IKTokenizer和IKAnalyzer兩個類。

 1 package kidsearch;
 2 import java.io.IOException;
 3 import java.io.Reader;
 4 
 5 import org.apache.lucene.analysis.Tokenizer;
 6 import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
 7 import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
 8 import org.apache.lucene.analysis.tokenattributes.TypeAttribute;
 9 import org.wltea.analyzer.core.IKSegmenter;
10 import org.wltea.analyzer.core.Lexeme;
11 
12 public class MyIKTokenizer extends Tokenizer {
13     // IK分詞器實現
14     private IKSegmenter _IKImplement;
15 
16     // 詞元文本屬性
17     private final CharTermAttribute termAtt;
18     // 詞元位移屬性
19     private final OffsetAttribute offsetAtt;
20     // 詞元分類屬性(該屬性分類參考org.wltea.analyzer.core.Lexeme中的分類常量)
21     private final TypeAttribute typeAtt;
22     // 記錄最后一個詞元的結束位置
23     private int endPosition;
24 
25     public MyIKTokenizer(Reader in) {
26         this(in, true);
27     }
28 
29     public MyIKTokenizer(Reader in, boolean useSmart) {
30         offsetAtt = addAttribute(OffsetAttribute.class);
31         termAtt = addAttribute(CharTermAttribute.class);
32         typeAtt = addAttribute(TypeAttribute.class);
33         _IKImplement = new IKSegmenter(input, useSmart);
34     }
35 
36     @Override
37     public boolean incrementToken() throws IOException {
38         // 清除所有的詞元屬性
39         clearAttributes();
40         Lexeme nextLexeme = _IKImplement.next();
41         if (nextLexeme != null) {
42             // 將Lexeme轉成Attributes
43             // 設置詞元文本
44             termAtt.append(nextLexeme.getLexemeText());
45             // 設置詞元長度
46             termAtt.setLength(nextLexeme.getLength());
47             // 設置詞元位移
48             offsetAtt.setOffset(nextLexeme.getBeginPosition(),
49                     nextLexeme.getEndPosition());
50             // 記錄分詞的最后位置
51             endPosition = nextLexeme.getEndPosition();
52             // 記錄詞元分類
53             typeAtt.setType(String.valueOf(nextLexeme.getLexemeType()));
54             // 返會true告知還有下個詞元
55             return true;
56         }
57         // 返會false告知詞元輸出完畢
58         return false;
59     }
60 
61     public void reset() throws IOException {
62         super.reset();
63         _IKImplement.reset(input);
64     }
65 
66     @Override
67     public final void end() {
68         // set final offset
69         int finalOffset = correctOffset(this.endPosition);
70         offsetAtt.setOffset(finalOffset, finalOffset);
71     }
72 
73 }
MyIKTokenizer
 1 package kidsearch;
 2 import java.io.Reader;
 3 import java.io.StringReader;
 4 
 5 import org.apache.lucene.analysis.Analyzer;
 6 import org.apache.lucene.util.IOUtils;
 7 import kidsearch.MyIKTokenizer;
 8 public class MyIkAnalyzer extends Analyzer {
 9 
10     @Override
11     protected TokenStreamComponents createComponents(String arg0) {
12         Reader reader=null;
13         try{
14             reader=new StringReader(arg0);
15             MyIKTokenizer it = new MyIKTokenizer(reader);
16             return new Analyzer.TokenStreamComponents(it);
17         }finally {
18             IOUtils.closeWhileHandlingException(reader);
19         }
20     }
21 
22 }
MyIKAnalyzer

參考的博客里有一部分是錯誤的

於是我又下載了IKAnalyzer的源碼,仔細看了一下Lexeme.java,發現沒有這個方法,只有getLexemeType,而且返回值是int,於是自己做了點小改動,終於編譯通過了!

值得注意的是,MyIKTokenizer里

1 public MyIKTokenizer(Reader in) {
2         this(in, true);
3     }
View Code

true為選擇智能划分(北京師范大學),而false為最細粒度划分(北京師范大學,北京,京師,師范大學,師范,大學)。

最后,要配置自己的停用詞和敏感詞。

自定義詞典一定要使用UTF-8無BOM編碼,否則不能實現過濾功能。

然后,在配置文件IKAnalyzer.cfg.xml里配置自定義詞典

最后,分別把所有的自定義詞典和IKAnalyzer.cfg.xml加到工程里的src(為了保險起見,我又把他們加到了bin里,IK的jar包里也加了)。

為了測試停用詞的效果,可以自己寫幾個小程序。

 1 import java.io.IOException;
 2 import java.io.StringReader;
 3 
 4 import org.apache.lucene.analysis.Analyzer;
 5 import org.apache.lucene.analysis.TokenStream;
 6 import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
 7 import org.wltea.analyzer.cfg.Configuration;
 8 import org.wltea.analyzer.cfg.DefaultConfig;
 9 import org.wltea.analyzer.core.IKSegmenter;
10 import org.wltea.analyzer.core.Lexeme;
11 import org.wltea.analyzer.lucene.IKAnalyzer;
12 
13 public class OwnIKAnalyzer {
14     public static void main(String[] args) throws IOException {  
15         String text="我有一個紅紅的蘋果";
16         StringReader sr=new StringReader(text);  
17     //    IKSegmenter ik=new IKSegmenter(sr, true);  
18         IKSegmenter ik=new IKSegmenter(sr,true); 
19         Lexeme lex=null;  
20         while((lex=ik.next())!=null){  
21             System.out.print(lex.getLexemeText()+",");  
22         }  
23 //        String text = "這是一個紅紅的蘋果";  
24 //        Configuration configuration = DefaultConfig.getInstance();  
25 //        configuration.setUseSmart(true);  
26 //        IKSegmenter ik = new IKSegmenter(new StringReader(text), configuration);  
27 //        Lexeme lexeme = null;  
28 //        while ((lexeme = ik.next()) != null) {  
29 //            System.out.println(lexeme.getLexemeText());  
30         }  
31     }  
View Code

測試結果為:(詞典里並沒有過濾“我”)

另外,IKAnalyzer可以配置自己的擴展詞典,比如“你的名字”本來會被分詞為“你,的,名字”,但是在ext.dic里加入“你的名字”后就是一個完整的整體,不會被切分了!

關於IKAnalyzer詞語過濾的功能今天就做了多,以后還會繼續補充~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM