1.1
Analyzer
使用时机
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.2
Analyzer
执行过程
1.2.1
代码分析
Analyzer
是一个抽象类,在Lucene
的lucene-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
字符流开始,创建一个基于Reader
的Tokenizer
分词器,经过三个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 China
,love
和 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.dic
和ext_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实战陆续发布中………
扫描下方二维码关注公众号 ↓↓↓↓↓↓↓↓↓↓