IKAnalyzer是一个开源基于JAVA语言的轻量级的中文分词第三方工具包,采用了特有的“正向迭代最细粒度切分算法“,支持细粒度和智能分词两种切分模式。刚开始使用的时候,发现不能支持中文和字母混合的分词,例如:iPhone5s土豪金。后来发现在2012版本,词典支持中文,英文,数字混合词语,并且优化了词典存储,内存更小的占用。支持用户词典扩展定义。为了更好的测试,这里就使用了IKAnalyzer2012_u6这个版本。
把 IKAnalyzer中的IKAnalyzer.cfg.xml, ext.dic(如果找不到,可以手动创建一个该文件), stopword.dic文件放到代码的根目录中。
下载下来的Jar包是包含了结合Lucene使用的例子,先把要检索的内容,写入Lucene索引,然后根据需要查找的关键词,通过Lucene的QueryParser对象进行解析查找,构造该QueryParser对象的时候,传入了IKAnalyzer,进而通过IKAnalyzer进行分词:
Analyzer analyzer = new IKAnalyzer(true); QueryParser qp = new QueryParser(Version.LUCENE_34, fieldName, analyzer);
ext.dic词典如下:
iPhone5s土豪金 2014巴西世界杯
完整代码如下:
// 使用Lucene分词 //Lucene Document的域名 String fieldName = "text"; //检索内容 String text = "据说WWDC要推出iPhone6要出了?与iPhone5s土豪金相比怎样呢?"; //实例化IKAnalyzer分词器 Analyzer analyzer = new IKAnalyzer(true); Directory directory = null; IndexWriter iwriter = null; IndexReader ireader = null; IndexSearcher isearcher = null; try { //建立内存索引对象 directory = new RAMDirectory(); //配置IndexWriterConfig IndexWriterConfig iwConfig = new IndexWriterConfig(Version.LUCENE_34 , analyzer); iwConfig.setOpenMode(OpenMode.CREATE_OR_APPEND); iwriter = new IndexWriter(directory , iwConfig); //写入索引 Document doc = new Document(); doc.add(new Field("ID", "10000", Field.Store.YES, Field.Index.NOT_ANALYZED)); doc.add(new Field(fieldName, text, Field.Store.YES, Field.Index.ANALYZED)); iwriter.addDocument(doc); iwriter.close(); //搜索过程********************************** //实例化搜索器 ireader = IndexReader.open(directory); isearcher = new IndexSearcher(ireader); String keyword = "iPhone5s土豪金"; //使用QueryParser查询分析器构造Query对象 QueryParser qp = new QueryParser(Version.LUCENE_34, fieldName, analyzer); qp.setDefaultOperator(QueryParser.AND_OPERATOR); Query query = qp.parse(keyword); System.out.println("Query = " + query); //搜索相似度最高的5条记录 TopDocs topDocs = isearcher.search(query , 5); System.out.println("命中:" + topDocs.totalHits); //输出结果 ScoreDoc[] scoreDocs = topDocs.scoreDocs; for (int i = 0; i < topDocs.totalHits; i++){ Document targetDoc = isearcher.doc(scoreDocs[i].doc); System.out.println("内容:" + targetDoc.toString()); } } catch (CorruptIndexException e) { e.printStackTrace(); } catch (LockObtainFailedException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); } finally{ if(ireader != null){ try { ireader.close(); } catch (IOException e) { e.printStackTrace(); } } if(directory != null){ try { directory.close(); } catch (IOException e) { e.printStackTrace(); } } }
执行结果:
加载扩展词典:ext.dic 加载扩展停止词典:stopword.dic Query = text:iphone5s土豪金 命中:1 内容:Document<stored,indexed<ID:10000> stored,indexed,tokenized<text:据说WWDC要推出iPhone6要出了?与iPhone5s土豪金相比怎样呢?>>
如果我们不需要建立Lucene索引文件,而是单纯的对一段文本进行分词,可以直接创建一个org.apache.lucene.analysis.Analyzer分词对象(org.wltea.analyzer.lucene.IKAnalyzer IK分词主类,基于Lucene的Analyzer接口实现)进行遍历分词数据。
下面演示下,并且在分词之前额外的添加一些单词到字典中。ext.dic词典如下:
iPhone5s土豪金 2014巴西世界杯
代码如下:
// 检索内容 String text = "据说WWDC要推出iPhone6要出了?与iPhone5s土豪金相比怎样呢?@2014巴西世界杯 test中文"; List<String> list = new ArrayList<String>(); list.add("test中文"); // 尚未初始化,因为第一次执行分词的时候才会初始化,为了在执行分此前手动添加额外的字典,需要先手动的初始化一下 Dictionary.initial(DefaultConfig.getInstance()); Dictionary.getSingleton().addWords(list); //创建分词对象 Analyzer analyzer = new IKAnalyzer(true); StringReader reader = new StringReader(text); TokenStream ts = analyzer.tokenStream("", reader); CharTermAttribute term = ts.getAttribute(CharTermAttribute.class); //遍历分词数据 try { while(ts.incrementToken()){ System.out.print(term.toString()+"|"); } } catch (IOException e) { e.printStackTrace(); } finally{ reader.close(); }
执行结果:
加载扩展词典:ext.dic 加载扩展停止词典:stopword.dic 据说|wwdc|要|推出|iphone6|要|出了|与|iphone5s土豪金|相比|怎样|呢|2014巴西世界杯|test中文|
另外,如果想不结合Lucene(不使用lucene-core-3.6.0.jar),而是仅仅单独的使用IKAnalyzer,可以直接使用IK分词器的核心类,真正分词的实现类IKSegmenter分词器进行分词,代码如下:
// 单独使用 // 检索内容 String text = "据说WWDC要推出iPhone6要出了?与iPhone5s相比怎样呢?@2014巴西世界杯"; // 创建分词对象 StringReader reader = new StringReader(text); IKSegmenter ik = new IKSegmenter(reader,true);// 当为true时,分词器进行最大词长切分 Lexeme lexeme = null; try { while((lexeme = ik.next())!=null) System.out.println(lexeme.getLexemeText()); } catch (IOException e) { e.printStackTrace(); } finally{ reader.close(); }
检测目标:在单独使用IKAnalyzer的情况下,尽量往扩展字典添加词组,测试十几万长度的文本的分词效率。
扩展词库添加搜狗词库:
词条 | 大小 | 检索内容字数 |
---|---|---|
392790个 | 13737KB | 158453 |
代码如下:
// 计算载入字典时间 long startLoadDict = System.currentTimeMillis(); Dictionary.initial(DefaultConfig.getInstance()); long endLoadDict = System.currentTimeMillis(); // 创建分词对象 StringReader reader = new StringReader(text); Lexeme lexeme = null; int hintTimes = 0; IKSegmenter ik = new IKSegmenter(reader,true);// 当为true时,分词器进行最大词长切分 long start = System.currentTimeMillis(); try { while((lexeme = ik.next())!=null) hintTimes ++; } catch (IOException e) { e.printStackTrace(); } finally{ reader.close(); } long end = System.currentTimeMillis(); System.out.println("载入字典时间:" + (endLoadDict - startLoadDict)/1000.0); System.out.println("处理文本字数:" + text.length()); System.out.println("获取词元次数:" + hintTimes); System.out.println("执行总时间:" + (end - start)/1000.0 + "s"); System.out.println("处理速度:" + text.length() / ((end - start)/1000.0) + "字/秒"); System.out.println("本次获取词元速度:" + hintTimes / ((end - start)/1000.0) + "词/秒");
IKAnalyzer本身有27W的词库,加上扩展词典,经过优化的方式存储到内存空间中的。
结果:
加载扩展词典:ext.dic 加载扩展停止词典:stopword.dic 载入字典时间:2.51 处理文本字数:158453 获取词元次数:54424 执行总时间:0.46s 处理速度:344463.04347826086字/秒 本次获取词元速度:118313.04347826086词/秒
======================================================================
原文地址: http://www.itzhai.com/ikanalyzer-lucene-demo-performance-test.html