文章转载自:http://www.cnblogs.com/dennisit/archive/2013/04/07/3005847.html

方案一: 基于配置的词典扩充

项目结构图如下:

IK分词器还支持通过配置IKAnalyzer.cfg.xml文件来扩充您的专有词典。谷歌拼音词库下载: http://ishare.iask.sina.com.cn/f/14446921.html?from=like
在web项目的src目录下创建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">/dicdata/use.dic.dic;/dicdata/googlepy.dic</entry>
<!-- 用户可以在这里配置自己的扩展停止词字典 -->
<entry key="ext_stopwords">/dicdata/ext_stopword.dic</entry> </properties>

词典文件的编辑与部署
分词器的词典文件格式是无BOM 的UTF-8 编码的中文文本文件,文件扩展名不限。词典中,每个中文词汇独立占一行,使用\r\n 的DOS 方式换行。(注,如果您不了解什么是无BOM 的UTF-8 格式, 请保证您的词典使用UTF-8 存储,并在文件的头部添加一空行)。您可以参考分词器源码org.wltea.analyzer.dic 包下的.dic 文件。词典文件应部署在Java 的资源路径下,即ClassLoader 能够加载的路径中。(推荐同IKAnalyzer.cfg.xml 放在一起).

方案二:基于API的词典扩充

在IKAnalyzer的与词条相关的操作
1.org.wltea.analyzer.cfg 
2.org.wltea.analyzer.dic

org.wltea.analyzer.cfg下Configuration接口中的定义
  getExtDictionarys() 获取扩展字典配置路径
  getExtStopWordDictionarys() 获取扩展停止词典配置路径
  getMainDictionary() 获取主词典路径
  getQuantifierDicionary() 获取量词词典路径
org.wltea.analyzer.cfg.DefualtConfig类是对Configuration接口的实现

org.wltea.analyzer.dic下的Directory类中相关的方法

public void addWords(java.util.Collection<java.lang.String> words)     批量加载新词条    参数:words - Collection词条列表
public void disableWords(java.util.Collection<java.lang.String> words) 批量移除(屏蔽)词条

Lucene中使用IKAnalyzer分词器实例演示
业务实体

package com.icrate.service.study.demo;
/**
*
*
* @version : 1.0
*
* @author : 苏若年 <a href="mailto:DennisIT@163.com">发送邮件</a>
*
* @since : 1.0 创建时间: 2013-4-7 下午01:52:49
*
* @function: TODO
*
*/
public class Medicine { private Integer id;
private String name;
private String function; public Medicine() { } public Medicine(Integer id, String name, String function) {
super();
this.id = id;
this.name = name;
this.function = function;
} //getter and setter() public String toString(){
return this.id + "," +this.name + "," + this.function;
}
}

构建模拟数据

package com.icrate.service.study.demo;

import java.util.ArrayList;
import java.util.List; /**
*
*
* @version : 1.0
*
* @author : 苏若年 <a href="mailto:DennisIT@163.com">发送邮件</a>
*
* @since : 1.0 创建时间: 2013-4-7 下午01:54:34
*
* @function: TODO
*
*/
public class DataFactory { private static DataFactory dataFactory = new DataFactory(); private DataFactory(){ } public List<Medicine> getData(){
List<Medicine> list = new ArrayList<Medicine>();
list.add(new Medicine(1,"银花 感冒颗粒","功能主治:银花感冒颗粒 ,头痛,清热,解表,利咽。"));
list.add(new Medicine(2,"感冒 止咳糖浆","功能主治:感冒止咳糖浆,解表清热,止咳化痰。"));
list.add(new Medicine(3,"感冒灵颗粒","功能主治:解热镇痛。头痛 ,清热。"));
list.add(new Medicine(4,"感冒灵胶囊","功能主治:银花感冒颗粒 ,头痛,清热,解表,利咽。"));
list.add(new Medicine(5,"仁和 感冒颗粒","功能主治:疏风清热,宣肺止咳,解表清热,止咳化痰。"));
return list; } public static DataFactory getInstance(){
return dataFactory;
}
}

使用Lucene对模拟数据进行检索

package com.icrate.service.study.demo;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List; import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.highlight.Formatter;
import org.apache.lucene.search.highlight.Fragmenter;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.Scorer;
import org.apache.lucene.search.highlight.SimpleFragmenter;
import org.apache.lucene.search.highlight.SimpleHTMLFormatter;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.wltea.analyzer.lucene.IKAnalyzer; /**
*
* LuenceProcess.java
*
* @version : 1.1
*
* @author : 苏若年 <a href="mailto:DennisIT@163.com">发送邮件</a>
*
* @since : 1.0 创建时间: Apr 3, 2013 11:48:11 AM
*
* TODO : Luence中使用IK分词器
*
*/ public class LuceneIKUtil { private Directory directory ;
private Analyzer analyzer ; /**
* 带参数构造,参数用来指定索引文件目录
* @param indexFilePath
*/
public LuceneIKUtil(String indexFilePath){
try {
directory = FSDirectory.open(new File(indexFilePath));
analyzer = new IKAnalyzer();
} catch (IOException e) {
e.printStackTrace();
}
} /**
* 默认构造,使用系统默认的路径作为索引
*/
public LuceneIKUtil(){
this("/luence/index");
} /**
* 创建索引
* Description:
* @author dennisit@163.com Apr 3, 2013
* @throws Exception
*/
public void createIndex()throws Exception{
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_35,analyzer);
IndexWriter indexWriter = new IndexWriter(directory,indexWriterConfig);
indexWriter.deleteAll();
List<Medicine> list = DataFactory.getInstance().getData();
for(int i=0; i<list.size(); i++){
Medicine medicine = list.get(i);
Document document = addDocument(medicine.getId(), medicine.getName(), medicine.getFunction());
indexWriter.addDocument(document);
} indexWriter.close();
} /**
*
* Description:
* @author dennisit@163.com Apr 3, 2013
* @param id
* @param title
* @param content
* @return
*/
public Document addDocument(Integer id, String name, String function){
Document doc = new Document();
//Field.Index.NO 表示不索引
//Field.Index.ANALYZED 表示分词且索引
//Field.Index.NOT_ANALYZED 表示不分词且索引
doc.add(new Field("id",String.valueOf(id),Field.Store.YES,Field.Index.NOT_ANALYZED));
doc.add(new Field("name",name,Field.Store.YES,Field.Index.ANALYZED));
doc.add(new Field("function",function,Field.Store.YES,Field.Index.ANALYZED));
return doc;
} /**
*
* Description: 更新索引
* @author dennisit@163.com Apr 3, 2013
* @param id
* @param title
* @param content
*/
public void update(Integer id,String title, String content){
try {
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_35,analyzer);
IndexWriter indexWriter = new IndexWriter(directory,indexWriterConfig);
Document document = addDocument(id, title, content);
Term term = new Term("id",String.valueOf(id));
indexWriter.updateDocument(term, document);
indexWriter.close();
} catch (Exception e) {
e.printStackTrace();
}
} /**
*
* Description:按照ID进行索引
* @author dennisit@163.com Apr 3, 2013
* @param id
*/
public void delete(Integer id){
try {
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_35,analyzer);
IndexWriter indexWriter = new IndexWriter(directory,indexWriterConfig);
Term term = new Term("id",String.valueOf(id));
indexWriter.deleteDocuments(term);
indexWriter.close();
} catch (Exception e) {
e.printStackTrace();
}
} /**
*
* Description:查询
* @author dennisit@163.com Apr 3, 2013
* @param where 查询条件
* @param scoreDoc 分页时用
*/
public List<Medicine> search(String[] fields,String keyword){ IndexSearcher indexSearcher = null;
List<Medicine> result = new ArrayList<Medicine>(); try {
//创建索引搜索器,且只读
IndexReader indexReader = IndexReader.open(directory,true);
indexSearcher = new IndexSearcher(indexReader); MultiFieldQueryParser queryParser =new MultiFieldQueryParser(Version.LUCENE_35, fields,analyzer);
Query query = queryParser.parse(keyword); //返回前number条记录
TopDocs topDocs = indexSearcher.search(query, 10);
//信息展示
int totalCount = topDocs.totalHits;
System.out.println("共检索出 "+totalCount+" 条记录"); //高亮显示
/*
创建高亮器,使搜索的结果高亮显示
SimpleHTMLFormatter:用来控制你要加亮的关键字的高亮方式
此类有2个构造方法
1:SimpleHTMLFormatter()默认的构造方法.加亮方式:<B>关键字</B>
2:SimpleHTMLFormatter(String preTag, String postTag).加亮方式:preTag关键字postTag
*/
Formatter formatter = new SimpleHTMLFormatter("<font color='red'>","</font>");
/*
QueryScorer
QueryScorer 是内置的计分器。计分器的工作首先是将片段排序。QueryScorer使用的项是从用户输入的查询中得到的;
它会从原始输入的单词、词组和布尔查询中提取项,并且基于相应的加权因子(boost factor)给它们加权。
为了便于QueryScoere使用,还必须对查询的原始形式进行重写。
比如,带通配符查询、模糊查询、前缀查询以及范围查询 等,都被重写为BoolenaQuery中所使用的项。
在将Query实例传递到QueryScorer之前,可以调用Query.rewrite (IndexReader)方法来重写Query对象
*/
Scorer fragmentScorer = new QueryScorer(query);
Highlighter highlighter = new Highlighter(formatter,fragmentScorer);
Fragmenter fragmenter = new SimpleFragmenter(100);
/*
Highlighter利用Fragmenter将原始文本分割成多个片段。
内置的SimpleFragmenter将原始文本分割成相同大小的片段,片段默认的大小为100个字符。这个大小是可控制的。
*/
highlighter.setTextFragmenter(fragmenter); ScoreDoc[] scoreDocs = topDocs.scoreDocs; for(ScoreDoc scDoc : scoreDocs){
Document document = indexSearcher.doc(scDoc.doc);
Integer id = Integer.parseInt(document.get("id"));
String name = document.get("name");
String function = document.get("function");
//float score = scDoc.score; //相似度 String lighterName = highlighter.getBestFragment(analyzer, "name", name);
if(null==lighterName){
lighterName = name;
} String lighterFunciton = highlighter.getBestFragment(analyzer, "function", function);
if(null==lighterFunciton){
lighterFunciton = function;
} Medicine medicine = new Medicine(); medicine.setId(id);
medicine.setName(lighterName);
medicine.setFunction(lighterFunciton); result.add(medicine);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
indexSearcher.close();
} catch (IOException e) {
e.printStackTrace();
}
} return result;
} public static void main(String[] args) {
LuceneIKUtil luceneProcess = new LuenceIKUtil("F:/index");
try {
luceneProcess.createIndex();
} catch (Exception e) {
e.printStackTrace();
}
//修改测试
luceneProcess.update(2, "测试内容", "修改测试。。。"); //查询测试
String [] fields = {"name","function"};
List<Medicine> list = luenceProcess.search(fields,"感冒");
for(int i=0; i<list.size(); i++){
Medicine medicine = list.get(i);
System.out.println("("+medicine.getId()+")"+medicine.getName() + "\t" + medicine.getFunction());
}
//删除测试
//luenceProcess.delete(1); }
}

程序运行结果

加载扩展词典:/dicdata/use.dic.dic
加载扩展词典:/dicdata/googlepy.dic
加载扩展停止词典:/dicdata/ext_stopword.dic
共检索出 4 条记录
(1)银花 <font color='red'>感冒</font>颗粒 功能主治:银花<font color='red'>感冒</font>颗粒 ,头痛,清热,解表,利咽。
(4)<font color='red'>感冒</font>灵胶囊 功能主治:银花<font color='red'>感冒</font>颗粒 ,头痛,清热,解表,利咽。
(3)<font color='red'>感冒</font>灵颗粒 功能主治:解热镇痛。头痛 ,清热。
(5)仁和 <font color='red'>感冒</font>颗粒 功能主治:疏风清热,宣肺止咳,解表清热,止咳化痰。

如何判断索引是否存在

    /**
* 判断是否已经存在索引文件
* @param indexPath
* @return
*/
private boolean isExistIndexFile(String indexPath) throws Exception{
File file = new File(indexPath);
if (!file.exists()) {
file.mkdirs();
}
String indexSufix="/segments.gen";
//根据索引文件segments.gen是否存在判断是否是第一次创建索引
File indexFile=new File(indexPath+indexSufix);
return indexFile.exists();
}

附录: IK分词处理过程

IK的整个分词处理过程首先,介绍一下IK的整个分词处理过程:

1. Lucene的分词基类是Analyzer,所以IK提供了Analyzer的一个实现类IKAnalyzer。首先,我们要实例化一个IKAnalyzer,它有一个构造方法接收一个参数isMaxWordLength,这个参数是标识IK是否采用最大词长分词,还是采用最细粒度切分两种分词算法。实际两种算法的实现,最大词长切分是对最细粒度切分的一种后续处理,是对最细粒度切分结果的过滤,选择出最长的分词结果。

2. IKAnalyzer类重写了Analyzer的tokenStream方法,这个方法接收两个参数,field name和输入流reader,其中filed name是Lucene的属性列,是对文本内容进行过分词处理和创建索引之后,索引对应的一个名称,类似数据库的列名。因为IK仅仅涉及分词处理,所以对field name没有进行任何处理,所以此处不做任何讨论。

3. tokenStream方法在Lucene对文本输入流reader进行分词处理时被调用,在IKAnalyzer的tokenStream方法里面仅仅实例化了一个IKTokenizer类,该类继承了Lucene的Tokenizer类。并重写了incrementToken方法,该方法的作用是处理文本输入流生成token,也就是Lucene的最小词元term,在IK里面叫做Lexeme。

4. 在IKtokenizer的构造方法里面实例化了IK里面最终要的分词类IKSegmentation,也称为主分词器。它的构造方法接收两个参数,reader和isMaxWordLength。

5. IKsegmentation的构造方法里面,主要做了三个工作,创建上下文对象Context,加载词典,创建子分词器。

6. Contex主要是存储分词结果集和记录分词处理的游标位置。

7. 词典是作为一个单例被创建的,主要有量词词典、主词典和停词词典。词典是被存储在字典片段类DictSegment 这个字典核心类里面的。DictSegment有一个静态的存储结构charMap,是公共词典表,用来存储所有汉字,key和value都是一个中文汉字,目前IK里面的charMap大概有7100多的键值对。另外,DictSegment还有两个最重要的数据结构,是用来存储字典树的,一个是DictSegment的数组childrenArray,另一个是key为单个汉字(每个词条的第一个汉字),value是DictSegment的HashMap childrenMap。这两个数据结构二者取其一,用来存储字典树。

8. 子分词器才是真正的分词类,IK里面有三个子分词器,量词分词器,CJK分词器(处理中文),停词分词器。主分词器IKSegmentation遍历这三个分词器对文本输入流进行分词处理。

9. IKTokenizer的incrementToken方法调用了IKSegmentation的next方法,next的作用是获得下一个分词结果。next在第一次被调用的时候,需要加载文本输入流,并将其读入buffer,此时便遍历子分词器,对buffer种的文本内容进行分词处理,然后把分词结果添加到context的lexemeSet中。

Lucene使用IKAnalyzer分词实例 及 IKAnalyzer扩展词库的更多相关文章

  1. IKAnalyzer 添加扩展词库和自定义词

    原文链接http://blog.csdn.net/whzhaochao/article/details/50130605 IKanalyzer分词器 IK分词器源码位置 http://git.osch ...

  2. solr配置同义词,停止词,和扩展词库(IK分词器为例)

    定义 同义词:搜索结果里出现的同义词.如我们输入”还行”,得到的结果包括同义词”还可以”. 停止词:在搜索时不用出现在结果里的词.比如is .a .are .”的”,“得”,“我” 等,这些词会在句子 ...

  3. solr添加中文IK分词器,以及配置自定义词库

    Solr是一个基于Lucene的Java搜索引擎服务器.Solr 提供了层面搜索.命中醒目显示并且支持多种输出格式(包括 XML/XSLT 和 JSON 格式).它易于安装和配置,而且附带了一个基于H ...

  4. JAVA 中配置IKAnalyzer扩展词库和停止词库

    1.后缀名.dic的词典文件,必须如使用文档里所说的 无BOM的UTF-8编码保存的文件.如果不确定什么是 无BOM的UTF-8编码,最简单的方式就是 用Notepad++编辑器打开,Encoding ...

  5. (转)全文检索技术学习(三)——Lucene支持中文分词

    http://blog.csdn.net/yerenyuan_pku/article/details/72591778 分析器(Analyzer)的执行过程 如下图是语汇单元的生成过程:  从一个Re ...

  6. paip.禁用IKAnalyzer 的默认词库.仅仅使用自定义词库.

    paip.禁用IKAnalyzer 的默认词库.仅仅使用自定义词库. 作者Attilax  艾龙,  EMAIL:1466519819@qq.com  来源:attilax的专栏 地址:http:// ...

  7. Sphinx(coreseek) 安装使用以及词库的扩展

    1.Sphinx(coreseek) 是啥 一般而言,Sphinx是一个独立的全文搜索引擎:而Coreseek是一个支持中文的全文搜索引擎,意图为其他应用提供高速.低空间占用.高结果相关度的中文全文搜 ...

  8. python jieba分词(结巴分词)、提取词,加载词,修改词频,定义词库 -转载

    转载请注明出处  “结巴”中文分词:做最好的 Python 中文分词组件,分词模块jieba,它是python比较好用的分词模块, 支持中文简体,繁体分词,还支持自定义词库. jieba的分词,提取关 ...

  9. IK词库扩展

    先写个标题,慢慢更新 默认的词库就算最小细粒度分词,很多名词也不会单字分词 比如:阿迪达斯,在IK是一个词,搜索每个字的单字关键词是无结果的,必须搜索阿迪达斯才有结果 所以我们需要扩展词库 IK官方教 ...

随机推荐

  1. 关于Android WindowManager显示悬浮窗的动画效果

    要实现WindowManager添加的窗口,实现动画显示,就需要添加如下红色的属性,其他的添加View只要设置其Animations属性也会实现动画,当然自己实现也可,但是能直接用系统的已经实现好的, ...

  2. C# 数据的加密解密

    /// <summary> /// 加密数据 /// </summary> /// <param name="Text"></param& ...

  3. CAKeyframeAnimation 旋转动画

  4. NSTimer定时器的用法

    #import "ViewController.h" @interface ViewController () { NSTimer *countDownTimer; int cou ...

  5. 纯CSS 多图片轮播

    今天做东西的时候,遇到一个问题关于图片轮播的问题,以前也接触过(百度 人家的demo改改..),再次遇到这个问题的时候,根据以前的印象找到了demo正信心满满的准备改一下嵌进去,发现 jquery.m ...

  6. 使用SourceTree

    SourceTree是个git的GUI软件. 教程将“使用Git”那个小节可视化,更为直观. 第一次启动的时候选择忽略,然后直接进入界面.  填入git地址与注册邮箱即可 例如mindset这本书的g ...

  7. MongoDB - MongoDB CRUD Operations, Query Documents, Project Fields to Return from Query

    By default, queries in MongoDB return all fields in matching documents. To limit the amount of data ...

  8. PHP与javascript实现变量交互

    <?php /** * 本例是PHP和javascript交互的例子,php中的值赋给js变量中,前提是这个php变量必须有值才行,就算是假分支中. * 比如php中的$flags在本例中为tr ...

  9. ASP.NET缓存全解析5:文件缓存依赖 转自网络原文作者李天平

    这种策略让缓存依赖于一个指定的文件,通过改变文件的更新日期来清除缓存. ///<summary> /// 获取当前应用程序指定CacheKey的Cache对象值 ///</summa ...

  10. JavaScript之延迟加载

    本文参阅http://www.appelsiini.net/projects/lazyload Javascript Lazyload延迟加载特效,有效降低HTPP连接次数,提高首屏加载时间 1.增加 ...