文章来源:http://blog.sina.com.cn/s/blog_700848850101gvt5.html
初始化最主要的工作就是读入词典,并将这些词放入内存字典树
1.main2012.dic(关键词)2.quantifier.dic(量词)3.stopword.dic(停用词)4.ext.dic(扩展词,可选)
http://blog.csdn.net/iamaboyy/article/details/7569977
1. 主流程
主要的就是ik.next()方法:
1) 读入待匹配的文本
2) 初始化文本指针,指向文本中的第一个字符
3) 遍历分词器,进行分词处理,这里是最核心的流程之一,将待匹配文本生成分词候选集。
——子分词器
4) 处理完一个字符之后,文本指针后移一位,直到处理完所有待匹配文本
——歧义处理
5) 生成分词候选集之后,进行歧义处理,歧义处理方法区分智能和非智能。
其功能是根据分词候选集和歧义处理策略,生成最后的分词结果
2. IK的子分词器
子分词器才是真正的分词类,IK里面有三个子分词器
i. CJKSegmenter(中文分词),
ii. CN_QuantifierSegmenter(数量词分词),
iii.LetterSegmenter(字母分词)。
主分词器IKSegmentation遍历这三个分词器对文本输入流进行的分词处理。每种分词器的分词方法是独立的,
生成自己的分词结果,放到分词候选集里
这三个分词器的代码很类似,思路都是一样的,采用字典树(CJK使用)或其他简单数据结构(CN_QuantifierSegmenter和LetterSegmenter)匹配文本中的当前字符,将匹配到的字符加入到分词候选集
其他两个词典与其主要区别如下:
1. CN_QuantifierSegmenter的词典来源两个地方:1.quantifier.dic文件,包含量词 2.数词直接写到ChnNumberChars类中了,内容如下:"一二两三四五六七八九十零壹贰叁肆伍陆柒捌玖拾百千万亿拾佰仟萬億兆卅廿"
2. LetterSegmenter分别有三个类似的处理器:字母、数字、字母和数字的组合。
处理的基本思路就是匹配连续的相同类型字符,直到出现不同类型字符为止,切出一个词,比如LetterSegmenter对字串"中文abc英文"的处理方式就是匹配出连续的字母子串abc,切为一个词,切词结果为中文 abc 英文
处理无法切分的非词典中词语,以及停用词
——http://blog.chinaunix.net/uid-20761674-id-3423941.html
这里有详细的流程图和url图
http://blog.163.com/liaoxiangui@126/blog/static/7956964020130299518177/
以下是局部关注的部分
Thread3:线程3的详细代码以及综合的注释
public voidanalyze(AnalyzeContext context) {
if(CharacterUtil.CHAR_USELESS != context.getCurrentCharType()){
//如果content不是CHAR_USELESS字符————对应100行
//优先处理tmpHits中的hit
if(!this.tmpHits.isEmpty()){
//处理词段队列
Hit[] tmpArray = this.tmpHits.toArray(newHit[this.tmpHits.size()]);
for(Hit hit : tmpArray){
// 把字符添加到hit中,并重新计算其匹配状态; matchWithHit是指从已匹配的Hit中直接取出DictSegment,继续向下匹配
hit = Dictionary.getSingleton().matchWithHit(context.getSegmentBuff(), context.getCursor() , hit);
if(hit.isMatch()){
//begin-->end+字符 是一个完整的词,输出这个词;
Lexeme newLexeme = newLexeme(context.getBufferOffset() , hit.getBegin() , context.getCursor() - hit.getBegin() + 1 , Lexeme.TYPE_CNWORD);
context.addLexeme(newLexeme);
if(!hit.isPrefix()){
//begin-->end+字符 不是词前缀,hit不需要继续匹配,移除
this.tmpHits.remove(hit);
}
}
else if(hit.isUnmatch()){
//hit不是前缀也不是完整词 ,移除
this.tmpHits.remove(hit);}
}
}
//既不是完整的词也不是词缀,判断是不是单字
//对当前指针位置的字符进行单字匹配,在主词典中
Hit singleCharHit = Dictionary.getSingleton().matchInMainDict(context.getSegmentBuff(), context.getCursor(), 1);
if(singleCharHit.isMatch()){
//如果是单字成词
//输出当前的词
Lexeme newLexeme = newLexeme(context.getBufferOffset() , context.getCursor() , 1 , Lexeme.TYPE_CNWORD);
context.addLexeme(newLexeme);
//同时也是词前缀
if(singleCharHit.isPrefix()){
//前缀匹配则放入hit列表
this.tmpHits.add(singleCharHit);
}
}else if(singleCharHit.isPrefix()){
//首字为词前缀
//前缀匹配则放入hit列表
this.tmpHits.add(singleCharHit);
}
}
else{
//如果content是CHAR_USELESS字符
//清空队列
this.tmpHits.clear();
}
//判断缓冲区是否已经读完
if(context.isBufferConsumed()){
//清空队列
this.tmpHits.clear();
}
//判断是否锁定缓冲区
if(this.tmpHits.size() == 0){
context.unlockBuffer(SEGMENTER_NAME);
}else{
context.lockBuffer(SEGMENTER_NAME);
}
}
在此发现IK中的userSmart配置 在solr4.0中不生效
由于版本而导致的问题
首先是中文分词器。中文分词库注定悲剧的地方在于总要跟着国外开源软件的更新走,一旦lucene,solr更新了,某些基础类改变或者接口变 动,分词器可能就不再支持了。IKTokenizerFactory继承自solr的BaseTokenizerFactory,但是 BaseTokenizerFactory在solr3.6之后版本已经不存在了,如果你按照以前的配置方式在scheme里配置:
https://code.google.com/p/ik-analyzer/issues/detail?id=125
https://code.google.com/p/ik-analyzer/issues/detail?id=91
<fieldType name="text_ika" class="solr.TextField" >
<analyzer type="index">
<tokenizer class="org.wltea.analyzer.lucene.IKAnalyzerSolrFactory" useSmart="false"/>
</analyzer>
<analyzer type="query">
<tokenizer class="org.wltea.analyzer.lucene.IKAnalyzerSolrFactory" useSmart="true"/>
</analyzer>
</fieldType>