【lucene系列学习四】使用IKAnalyzer分词器实现敏感词和停用词过滤
Lucene自带的中文分词器SmartChineseAnalyzer不太好扩展,于是我用了IKAnalyzer来进行敏感词和停用词的过滤。
首先,下载IKAnalyzer,我下载了
然后,由于IKAnalyzer已经很久不更新了,不兼容现在的Lucene6版本,所以我参考网上的资料,重写了IKTokenizer和IKAnalyzer两个类。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
参考的博客里有一部分是错误的
于是我又下载了IKAnalyzer的源码,仔细看了一下Lexeme.java,发现没有这个方法,只有getLexemeType,而且返回值是int,于是自己做了点小改动,终于编译通过了!
值得注意的是,MyIKTokenizer里
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public MyIKTokenizer(Reader in) { 2 this(in, true); 3 }
true为选择智能划分(北京师范大学),而false为最细粒度划分(北京师范大学,北京,京师,师范大学,师范,大学)。
最后,要配置自己的停用词和敏感词。
自定义词典一定要使用UTF-8无BOM编码,否则不能实现过滤功能。
然后,在配置文件IKAnalyzer.cfg.xml里配置自定义词典
最后,分别把所有的自定义词典和IKAnalyzer.cfg.xml加到工程里的src(为了保险起见,我又把他们加到了bin里,IK的jar包里也加了)。
为了测试停用词的效果,可以自己写几个小程序。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
测试结果为:(词典里并没有过滤“我”)
另外,IKAnalyzer可以配置自己的扩展词典,比如“你的名字”本来会被分词为“你,的,名字”,但是在ext.dic里加入“你的名字”后就是一个完整的整体,不会被切分了!
关于IKAnalyzer词语过滤的功能今天就做了多,以后还会继续补充~