IK正向迭代最细粒度切分算法流程

文章来源:http://blog.sina.com.cn/s/blog_700848850101gvt5.html


IK正向迭代最细粒度切分算法流程

一、      IK分词初始化

初始化最主要的工作就是读入词典,并将这些词放入内存字典树

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_QuantifierSegmenterLetterSegmenter)匹配文本中的当前字符,将匹配到的字符加入到分词候选集 

其他两个词典与其主要区别如下:

1. CN_QuantifierSegmenter的词典来源两个地方:1.quantifier.dic文件,包含量词 2.数词直接写到ChnNumberChars类中了,内容如下:"一二两三四五六七八九十零壹贰叁肆伍陆柒捌玖拾百千万亿拾佰仟萬億兆卅廿"

2. LetterSegmenter分别有三个类似的处理器:字母、数字、字母和数字的组合。

处理的基本思路就是匹配连续的相同类型字符,直到出现不同类型字符为止,切出一个词,比如LetterSegmenter对字串"中文abc英文"的处理方式就是匹配出连续的字母子串abc,切为一个词,切词结果为中文 abc 英文 

三、对相交词进行歧义处理(judge方法)
四、其他

处理无法切分的非词典中词语,以及停用词

——http://blog.chinaunix.net/uid-20761674-id-3423941.html


这里有详细的流程图和url图

http://blog.163.com/liaoxiangui@126/blog/static/7956964020130299518177/


以下是局部关注的部分

IK分词源码分析整理学习

  1. Thread1——main(String[])
  2. Thread2——IKSegmenter.next()
  3. 测试类中IKSegmenter ik=new IKSegmenter(sr, false);  //true代表调用IKSegmenter()构造函数时使用智能分词                调用IKSegmenter
  4. IKSegmenter.next()主流程最主要的就是.next()方法:
  5. //遍历子分词器  for(ISegmenter segmenter : segmenters){                                          segmenter.analyze(context);}
  6. 子分词器for的第一次循环:处理英文,字母,阿拉伯字母,混合字母的analyze()
  7. 子分词器for的第二次循环:处理中文数词,中文量词的analyze()
  8. Thread3——CJKSegmenter.analyze(AnalyzeContext) 
  9. 子分词器for的第三次循环:中文-日韩文子分词器analyze()
  10. 重置子分词器for(ISegmenter segmenter : segmenters){ segmenter.reset();}
  11. 歧义处理this.arbitrator.process(context, this.cfg.useSmart());


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{

         //如果contentCHAR_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里配置:

  1. <fieldType name="text" class="solr.TextField" >   
  2.   <analyzer type="index">   
  3.     <tokenizer class="org.wltea.analyzer.solr.IKTokenizerFactory" useSmart ="false"/>   
  4.   </analyzer>   
  5.   <analyzer type="query">   <tokenizer class="org.wltea.analyzer.solr.IKTokenizerFactory" useSmart ="false"/>   
  6.   </analyzer>   
  7. </fieldType>  
启动solr后报错提示BaseTokenizerFactory类是找不到了的。
http://blog.csdn.net/pelick/article/details/8469442

解决办法:

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>



  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值