public void analyze(AnalyzeContext context) { if(CharacterUtil.CHAR_USELESS != context.getCurrentCharType()){ //优先处理tmpHits中的hit if(!this.tmpHits.isEmpty()){ //处理词段队列 Hit[] tmpArray = this.tmpHits.toArray(new Hit[this.tmpHits.size()]); for(Hit hit : tmpArray){ hit = Dictionary.getSingleton().matchWithHit(context.getSegmentBuff(), context.getCursor() , hit); if(hit.isMatch()){ char[] date = context.getSegmentBuff(); //输出当前的词 Lexeme newLexeme = new Lexeme(context.getBufferOffset() , hit.getBegin() , context.getCursor() - hit.getBegin() + 1 , Lexeme.TYPE_CNWORD); context.addLexeme(newLexeme); if(!hit.isPrefix()){//不是词前缀,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 = new Lexeme(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{ //遇到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); } } <span style="font-family:幼圆;font-size:18px;"></span>
逐句来分析:
if(CharacterUtil.CHAR_USELESS != context.getCurrentCharType())
Hit singleCharHit = Dictionary.getSingleton().matchInMainDict(context.getSegmentBuff(), context.getCursor(), 1);
public class DictSegment implements Comparable<DictSegment>{ //公用字典表,存储汉字 private static final Map<Character , Character> charMap = new HashMap<Character , Character>(16 , 0.95f); //数组大小上限 private static final int ARRAY_LENGTH_LIMIT = 3; //Map存储结构 private Map<Character , DictSegment> childrenMap; //数组方式存储结构 private DictSegment[] childrenArray; //当前节点上存储的字符 //private Character nodeChar; public Character nodeChar; //当前节点存储的Segment数目 //storeSize <=ARRAY_LENGTH_LIMIT ,使用数组存储, storeSize >ARRAY_LENGTH_LIMIT ,则使用Map存储 private int storeSize = 0; //当前DictSegment状态 ,默认 0 , 1表示从根节点到当前节点的路径表示一个词 private int nodeState = 0;
if(singleCharHit.isMatch()){//首字成词 //输出当前的词 Lexeme newLexeme = new Lexeme(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); }
if(!this.tmpHits.isEmpty()
核心思路其实很简单,CN_QuantifierSegmenter和LetterSegmenter的分词代码不再详细描述,代码很CJK很类似,主要区别如下:
1. CN_QuantifierSegmenter的词典来源两个地方:1.quantifier.dic文件,包含量词 2.数词直接写到ChnNumberChars类中了,内容如下:"一二两三四五六七八九十零壹贰叁肆伍陆柒捌玖拾百千万亿拾佰仟萬億兆卅廿"
2. LetterSegmenter分别有三个类似的处理器:字母、数字、字母和数字的组合。
3. 处理的基本思路就是匹配连续的相同类型字符,直到出现不同类型字符为止,切出一个词,比如LetterSegmenter对字串"中文abc英文"的处理方式就是匹配出连续的字母子串abc,切为一个词,切词结果为中文 abc 英文
切词不难理解吧,后面进入另一个核心内容:歧义处理IK分词源码分析连载(三)--歧义处理