ik源码分析之我见
?最近对lucene的分词有比较浓厚的兴趣,对ik的源码研究了一段时间
?
?ik对分词的处理参照DictSegment类的match方法
? DictSegment类会先构建一个类似tree的结构,tree的根节点是以字典里的所有词的首字符为入口,后续的每个字符都是一个节点,如果该子节点已经是字典里的某个词的结束符将DictSegment(每个节点的bean类型)的nodeState的属性置为1(表示要匹配的词是一个分词),否则为0。
在match方法中
int available = fillBuffer(input);int buffIndex = 0;for( ; buffIndex < available ; buffIndex++){ //移动缓冲区指针 context.setCursor(buffIndex); //进行字符规格化(全角转半角,大写转小写处理) segmentBuff[buffIndex] = CharacterHelper.regularize(segmentBuff[buffIndex]); //遍历子分词器 for(ISegmenter segmenter : segmenters){ segmenter.nextLexeme(segmentBuff , context); } /* * 满足一下条件时, * 1.available == BUFF_SIZE 表示buffer满载 * 2.buffIndex < available - 1 && buffIndex > available - BUFF_EXHAUST_CRITICAL表示当前指针处于临界区内 * 3.!context.isBufferLocked()表示没有segmenter在占用buffer * 要中断当前循环(buffer要进行移位,并再读取数据的操作) */ if(available == BUFF_SIZE && buffIndex < available - 1 && buffIndex > available - BUFF_EXHAUST_CRITICAL && !context.isBufferLocked()){ break; } }
?在for(ISegmenter segmenter : segmenters){
????? segmenter.nextLexeme(segmentBuff , context);}
这个过程中各个子segmenter(包括 中文处理ChineseSegmenter ,英文处理LetterSegmenter,QuantifierSegmenter)都会对字符进行分析,这里我们主要讨论ChineseSegmenter。在ChineseSegmenter 中nextLexeme方法中接收IKSegmentation.next方法传入的字符和context,context的cursor属性每次传入都会加一,表示要分析的字符在缓存数据中的偏移量。
? 1 如果从该缓存数据的开始位置到该字符串为止组成的一个字符串(A)是字典里的一个词那将会添加到context的Lexeme中,表示这是一个分析后的词,
? 2 如果A是某个词的前缀,比如(中华人就是中华人民的前缀)将不会做特殊处理,等待下个字符进入(例子中的 民 )组成一个 中华人民 再添加到lexeme中。
? 3? 如果该字符是前缀那将添加到 ChineseSegmenter 中的_CSegList变量,该变量保存了所有已经分析了的并且是前缀的字符和它在缓存中的偏移量(被移除_CSegList的条件是不是A不是字典里的词 或 A是字典的一个词但A不是某个词的前缀,移除避免了_CSegList持续增长和重复的检查)
?4 如果该字符不是前缀做其他的处理,这里暂不讨论
说到现在读者应该明白了为什么上面提出的问题了吧。
不过ik的处理方式还有个问题,那就是一个文档足够大,如果一个词刚好在两个缓存中间那该词将不会得到
?