IKAnalyzer源码分析---2
来源:互联网 发布:全球实时网络攻击 编辑:程序博客网 时间:2024/06/05 22:58
IKAnalyzer源码分析—incrementToken
上一章分析了IKAnalyzer的初始化,本章开始分析incrementToken函数,该函数是IKAnalyzer分词的主要函数。
TokenStream::incrementToken
public boolean incrementToken() throws IOException { clearAttributes(); Lexeme nextLexeme = _IKImplement.next(); if(nextLexeme != null){ termAtt.append(nextLexeme.getLexemeText()); termAtt.setLength(nextLexeme.getLength()); offsetAtt.setOffset(nextLexeme.getBeginPosition(), nextLexeme.getEndPosition()); endPosition = nextLexeme.getEndPosition(); typeAtt.setType(nextLexeme.getLexemeTypeString()); return true; } return false; }
incrementToken函数一次获得一个词元,clearAttributes函数用来初始化参数,_IKImplement的next函数获取下一个词元nextLexeme,该函数也是incrementToken的核心函数,获得nextLexeme后,就将该词元的各个属性添加到lucene的Attribute结构中,termAtt用于保存词元,offsetAtt保存位置信息,typeAtt保存词元类型。
TokenStream::incrementToken->IKSegmentation::next
public synchronized Lexeme next()throws IOException{ Lexeme l = null; while((l = context.getNextLexeme()) == null ){ int available = context.fillBuffer(this.input); if(available <= 0){ context.reset(); return null; }else{ context.initCursor(); do{ for(ISegmenter segmenter : segmenters){ segmenter.analyze(context); } if(context.needRefillBuffer()){ break; } }while(context.moveCursor()); for(ISegmenter segmenter : segmenters){ segmenter.reset(); } } this.arbitrator.process(context, this.cfg.useSmart()); context.outputToResult(); context.markBufferOffset(); } return l; }
为了提高效率,next函数一次处理多个词元,然后将其保存在AnalyzeContext的缓存results列表中,一次获得一个词元。
首先通过getNextLexeme函数判断AnalyzeContext的缓存里是否有处理过的词元,如果有就直接返回该Lexeme,如果没有,首先调用fillBuffer函数将输入input填充到缓存里segmentBuff,如果没有读取到新的数据,并且上一次的所有数据都已经处理完毕,则直接返回,如果读取到新的数据,则调用initCursor初始化,然后遍历segmenters列表,并调用每个Segmenter的analyze函数进行处理,根据上一章的分析可知,这里会依次取出LetterSegmenter、CN_QuantifierSegmenter、CJKSegmenter进行处理。如果剩余的数据不足,或者读取完整个segmentBuff缓存,则跳出循环,否则通过moveCursor函数移动指针,读取下一个数据。
循环读取后,就通过reset函数重新初始化前面的三个Segmenter。然后调用IKArbitrator的process函数进行歧义处理。处理后,再通过AnalyzeContext的outputToResult函数将最终的输出结果保存在AnalyzeContext的result中,后续就可以直接通过getNextLexeme函数获取到了。
TokenStream::incrementToken->IKSegmentation::next->AnalyzeContext::getNextLexeme
Lexeme getNextLexeme(){ Lexeme result = this.results.pollFirst(); while(result != null){ this.compound(result); if(Dictionary.getSingleton().isStopWord(this.segmentBuff , result.getBegin() , result.getLength())){ result = this.results.pollFirst(); }else{ result.setLexemeText(String.valueOf(segmentBuff , result.getBegin() , result.getLength())); break; } } return result; }
成员变量results为Lexeme列表,Lexeme封装了解析玩的词元信息,首先从results中获取一个结果Lexeme,然后通过compound函数判断是否需要和下一个词元进行合并。如果该Lexeme对应的词元在停词列表中,则通过pollFirst继续获取下一个Lexeme,否则从成员变量segmentBuff(封装了文章对应的char数组)获取该词元文本并返回。
TokenStream::incrementToken->IKSegmentation::next->AnalyzeContext::getNextLexeme->compound
private void compound(Lexeme result){ if(Lexeme.TYPE_ARABIC == result.getLexemeType()){ Lexeme nextLexeme = this.results.peekFirst(); boolean appendOk = false; if(Lexeme.TYPE_CNUM == nextLexeme.getLexemeType()){ appendOk = result.append(nextLexeme, Lexeme.TYPE_CNUM); }else if(Lexeme.TYPE_COUNT == nextLexeme.getLexemeType()){ appendOk = result.append(nextLexeme, Lexeme.TYPE_CQUAN); } if(appendOk){ this.results.pollFirst(); } } if(Lexeme.TYPE_CNUM == result.getLexemeType() && !this.results.isEmpty()){ Lexeme nextLexeme = this.results.peekFirst(); boolean appendOk = false; if(Lexeme.TYPE_COUNT == nextLexeme.getLexemeType()){ appendOk = result.append(nextLexeme, Lexeme.TYPE_CQUAN); } if(appendOk){ this.results.pollFirst(); } } }
如果当前词元类型是数字(TYPE_ARABIC),并且下一个词元类型是中文数字(TYPE_CNUM),则将这两个词元合并并设置类型为中文数字,如果下一个词元类型时量词(TYPE_COUNT),则将这两个词元合并并设置类型为中文数量词(TYPE_CQUAN)。
如果第一次处理后的当前词元是中文数字,并且下一个词元类型时量词(TYPE_COUNT),则将这两个词元合并并设置类型为中文数量词(TYPE_CQUAN)。
TokenStream::incrementToken->IKSegmentation::next->fillBuffer
int fillBuffer(Reader reader) throws IOException{ int readCount = 0; if(this.buffOffset == 0){ readCount = reader.read(segmentBuff); }else{ int offset = this.available - this.cursor; if(offset > 0){ System.arraycopy(this.segmentBuff , this.cursor , this.segmentBuff , 0 , offset); readCount = offset; } readCount += reader.read(this.segmentBuff , offset , BUFF_SIZE - offset); } this.available = readCount; this.cursor = 0; return readCount; }
fillBuffer函数读取input数据到缓存segmentBuff中,假设不是第一次读取,则需要将上一次未处理完的数据一起处理,最后返回一共读取到的数据。
TokenStream::incrementToken->IKSegmentation::next->AnalyzeContext::needRefillBuffer
boolean needRefillBuffer(){ return this.available == BUFF_SIZE && this.cursor < this.available - 1 && this.cursor > this.available - BUFF_EXHAUST_CRITICAL && !this.isBufferLocked(); }
needRefillBuffer用来判断是否接近读取完segmentBuff缓存,判断条件是剩余的未读取的数据是否小于BUFF_EXHAUST_CRITICAL,默认值为100。
TokenStream::incrementToken->IKSegmentation::next->AnalyzeContext::outputToResult
void outputToResult(){ int index = 0; for( ; index <= this.cursor ;){ if(CharacterUtil.CHAR_USELESS == this.charTypes[index]){ index++; continue; } LexemePath path = this.pathMap.get(index); if(path != null){ Lexeme l = path.pollFirst(); while(l != null){ this.results.add(l); index = l.getBegin() + l.getLength(); l = path.pollFirst(); if(l != null){ for(;index < l.getBegin();index++){ this.outputSingleCJK(index); } } } }else{ this.outputSingleCJK(index); index++; } } this.pathMap.clear(); } private void outputSingleCJK(int index){ if(CharacterUtil.CHAR_CHINESE == this.charTypes[index]){ Lexeme singleCharLexeme = new Lexeme(this.buffOffset , index , 1 , Lexeme.TYPE_CNCHAR); this.results.add(singleCharLexeme); }else if(CharacterUtil.CHAR_OTHER_CJK == this.charTypes[index]){ Lexeme singleCharLexeme = new Lexeme(this.buffOffset , index , 1 , Lexeme.TYPE_OTHER_CJK); this.results.add(singleCharLexeme); } }
outputToResult遍历当前缓冲至当前指针cursor位置,如果某个char的类型为CHAR_USELESS,则直接忽略,否则从pathMap中获取LexemePath,LexemePath保存了经过歧义处理后的多个词元,如果LexemePath不存在,则直接通过outputSingleCJK单字输出,如果存在,则将其中的多个Lexeme添加到最终的results列表中,并且对该Lexeme至下一个Lexeme之间的缓存进行单字输出。
outputSingleCJK函数检查待输出的单字类型是否为CHAR_CHINESE或者CHAR_OTHER_CJK,然后将单字封装为Lexeme并添加到results列表中。
- IKAnalyzer源码分析---2
- IKAnalyzer源码分析---1
- IKAnalyzer源码分析---3
- IKAnalyzer源码分析---4
- IKanalyzer中文分词源码分析<一>字典的数据结构
- 读IKAnalyzer源码 之分词
- IKAnalyzer词典占用内存大小分析
- IKAnalyzer
- IKAnalyzer
- 读IKAnalyzer源码之IK启动
- IKAnalyzer中文分词分析内容目录
- 中文分词器IKAnalyzer——IKQueryParser主类分析
- Nutch1.2增加IKAnalyzer中文分词
- Nutch1.2 添加IKAnalyzer中文分词
- Nutch1.2 添加IKAnalyzer中文分词
- IKAnalyzer 基于Lucene4.2 的开发案例
- IKAnalyzer 基于Lucene4.2 的开发案例
- jboss4.2源码分析
- Array
- VS2012下利用Opengl在Cview中绘制
- IDEA使用技巧
- 【网络编程】南工聊天客户端源码
- Jquery绑定事件失效
- IKAnalyzer源码分析---2
- Android Activity做出弹窗样式
- html元素-表单元素及实用属性
- echo print_r var_dump
- Qt5.6 用SQLite数据库验证做登录框,并查删改xml文件做记住密码和自动登录<三>
- 前端学习之路——jQuery动画
- Windows下从源代码构建Spark
- thinkphp5.0链接mysql数据库(1)
- HTML5 学习笔记整理