`

IK的简单分析

 
阅读更多

一、首先简单的了解两个问题:

                                                1.Trie树的简单实现

                                                http://xiaozhou09.iteye.com/blog/1927348

                                                2.最大正向匹配的一个简单例子

          //正向匹配的一个例子
	public void wordSegment(String sentence){
		
		int strLength=sentence.length();//计算传入字符串的长度		
		int maxLength=12;//最大匹配的长度		
		int start=0,end=0;//start 是开始位置,end为结束位置		
	    boolean isFind=false;//是否存在这个词	
	    
	    while(start<strLength){
	    	//字符串结尾的地方
	    	int N=start+maxLength>strLength?strLength:start+maxLength;
	       //存在首先失败的
	    	isFind=false;
	    	String temp="";
	    	
	    	for(end=N;end>start;end--){
	    	   temp=sentence.substring(start, end);
                   //字典判断是否包含这个词
	    	  if(hs.contains(temp)){
	    		isFind=true;
	    		break;
	    	}
	    	}
	    	//找不到以这个字开头的词
	    	if(!isFind){
	    		end+=1;
	    		temp=sentence.substring(start,end);
	    	}
	    	
	    	System.out.println("开始位置:"+start+"==="+temp+"==="+"结束位置:"+end);
	    	start=end;
	    }
		
	}

 

 

二、IK的实现思路简单分析  我看的不是最新的,版本名字是:IK Analyzer 2012_u5_source

       1.对于文本的处理一个Read一边读取一边处理速度这样肯定是最快的

问题: 对于读取最后的边界的处理
==================================================
是否要进行读取Read的next的时候
/**
	 * 判断segmentBuff是否需要读取新数据
	 * 
	 * 满足一下条件时,
	 * 1.available == BUFF_SIZE 表示buffer满载
	 * 2.buffIndex < available - 1 && buffIndex > available - BUFF_EXHAUST_CRITICAL表示当前指针处于临界区内
	 * 3.!context.isBufferLocked()表示没有segmenter在占用buffer
	 * 要中断当前循环(buffer要进行移位,并再读取数据的操作)
	 * @return
	 */
	boolean needRefillBuffer(){
		return this.available == BUFF_SIZE 
			&& this.cursor < this.available - 1   
			&& this.cursor  > this.available - BUFF_EXHAUST_CRITICAL
			&& !this.isBufferLocked();
	}

我认为第二个条件的处理很关键

         2.IK在处理前的操作

 

 Context属性长度一致:

      //字符窜读取缓冲
    private char[] segmentBuff;
    //字符类型数组
    private int[] charTypes;
=========================
属性实例化:

  /**
     * 初始化buff指针,处理第一个字符
     */
    void initCursor(){
    	this.cursor = 0;
    	this.segmentBuff[this.cursor] = CharacterUtil.regularize(this.segmentBuff[this.cursor]);
    	this.charTypes[this.cursor] = CharacterUtil.identifyCharType(this.segmentBuff[this.cursor]);
    }
    
    /**
     * 指针+1
     * 成功返回 true; 指针已经到了buff尾部,不能前进,返回false
     * 并处理当前字符
     */
    boolean moveCursor(){
    	if(this.cursor < this.available - 1){
    		this.cursor++;
        	this.segmentBuff[this.cursor] = CharacterUtil.regularize(this.segmentBuff[this.cursor]);
        	this.charTypes[this.cursor] = CharacterUtil.identifyCharType(this.segmentBuff[this.cursor]);
    		return true;
    	}else{
    		return false;
    	}
    }

        3.IK单词的识别,首先看下对数字、单词、数字字母组合等

 

其中的数字识别:

属性:

             /*
	 * 阿拉伯数字起始位置
	 */
	private int arabicStart;
	
	/*
	 * 阿拉伯数字结束位置
	 */
	private int arabicEnd;

==============================

方法:
/**
	 * 处理阿拉伯数字输出
	 * @param context
	 * @return
	 */
	private boolean processArabicLetter(AnalyzeContext context){
		boolean needLock = false;
		
		if(this.arabicStart == -1){//当前的分词器尚未开始处理数字字符	
			if(CharacterUtil.CHAR_ARABIC == context.getCurrentCharType()){
				//记录起始指针的位置,标明分词器进入处理状态
				this.arabicStart = context.getCursor();
				this.arabicEnd = this.arabicStart;
			}
		}else {//当前的分词器正在处理数字字符	
			if(CharacterUtil.CHAR_ARABIC == context.getCurrentCharType()){
				//记录当前指针位置为结束位置
				this.arabicEnd = context.getCursor();
			}else if(CharacterUtil.CHAR_USELESS == context.getCurrentCharType()
					&& this.isNumConnector(context.getCurrentChar())){
				//不输出数字,但不标记结束
			}else{
				////遇到非Arabic字符,输出词元
                                                   //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                                                   //识别产生一个词元了
				Lexeme newLexeme = new Lexeme(context.getBufferOffset() , this.arabicStart , this.arabicEnd - this.arabicStart + 1 , Lexeme.TYPE_ARABIC);
				context.addLexeme(newLexeme);
				this.arabicStart = -1;
				this.arabicEnd = -1;
			}
		}
		
		//判断缓冲区是否已经读完
		if(context.isBufferConsumed()){
			if(this.arabicStart != -1 && this.arabicEnd != -1){
				//生成已切分的词元
				Lexeme newLexeme = new Lexeme(context.getBufferOffset() ,  this.arabicStart , this.arabicEnd - this.arabicStart + 1 , Lexeme.TYPE_ARABIC);
				context.addLexeme(newLexeme);
				this.arabicStart = -1;
				this.arabicEnd = -1;
			}
		}
		
		//判断是否锁定缓冲区
		if(this.arabicStart == -1 && this.arabicEnd == -1){
			//对缓冲区解锁
			needLock = false;
		}else{
			needLock = true;
		}
		
		System.out.println("数字:"+arabicStart+" "+arabicEnd);
		return needLock;		
	}	

	/**
	 * 判断是否是字母连接符号
	 * @param input
	 * @return
	 */
	private boolean isLetterConnector(char input){
		int index = Arrays.binarySearch(Letter_Connector, input);
		return index >= 0;
	}
	
	/**
	 * 判断是否是数字连接符号
	 * @param input
	 * @return
	 */
	private boolean isNumConnector(char input){
		int index = Arrays.binarySearch(Num_Connector, input);
		return index >= 0;
	}

       4.中文词组的识别要依靠字典了,怎么去识别?

 

//处理的时匹配是否可能有一个词组就是根的子节点是否包含它,有的话存放到一个List中
//下一次遍历List中存储的跟当前的链接在一起判断1.完全匹配  产生一个新单词
//                                     2.是前缀的话 继续保留List中 下次处理
// 这样识别的了字典里面的存在当前匹配的所有的词
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()){
						//输出当前的词
						Lexeme newLexeme = new Lexeme(context.getBufferOffset() , hit.getBegin() , context.getCursor() - hit.getBegin() + 1 , Lexeme.TYPE_CNWORD);
						context.addLexeme(newLexeme);
						  
						System.out.println("真正的匹配 "+new String(context.getSegmentBuff()).substring(hit.getBegin(),context.getCursor()+1));
						if(!hit.isPrefix()){//不是词前缀,hit不需要继续匹配,移除
							this.tmpHits.remove(hit);
						}
						
					}else if(hit.isUnmatch()){
						//hit不是词,移除
						this.tmpHits.remove(hit);
					}					
				}
			}			
			
			//System.out.println("first");
			//*********************************
			//再对当前指针位置的字符进行单字匹配
			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);
                 
				//System.out.println("   "+context.getSegmentBuff()[context.getCursor()]);
				//同时也是词前缀
				if(singleCharHit.isPrefix()){
					//前缀匹配则放入hit列表
					this.tmpHits.add(singleCharHit);
				}
				System.out.println("匹配"+context.getSegmentBuff()[context.getCursor()]);
			}else if(singleCharHit.isPrefix()){//首字为词前缀
				//前缀匹配则放入hit列表
				this.tmpHits.add(singleCharHit);
				System.out.println("前缀 "+context.getSegmentBuff()[context.getCursor()]);

			}

		}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);
		}
	}

         5.粒度实现后奇异消除的实现

1.首先第一轮的是有顺序的排列
 /*
     * 词元在排序集合中的比较算法
     * @see java.lang.Comparable#compareTo(java.lang.Object)
     */
	public int compareTo(Lexeme other) {
		//起始位置优先
        if(this.begin < other.getBegin()){
            return -1;
        }else if(this.begin == other.getBegin()){
        	//词元长度优先
        	if(this.length > other.getLength()){
        		return -1;
        	}else if(this.length == other.getLength()){
        		return 0;
        	}else {//this.length < other.getLength()
        		return 1;
        	}
        	
        }else{//this.begin > other.getBegin()
        	return 1;
        }
	}
2.把有歧义的最大长度串单独一个链出来
3.然后每一个歧义的词都作为一个一次存在的实现这个歧义链的一种情况
4.自动实现的排序取第一个最合适的
public int compareTo(LexemePath o) {
		//比较有效文本长度
		if(this.payloadLength > o.payloadLength){
			return -1;
		}else if(this.payloadLength < o.payloadLength){
			return 1;
		}else{
			//比较词元个数,越少越好
			if(this.size() < o.size()){
				return -1;
			}else if (this.size() > o.size()){
				return 1;
			}else{
				//路径跨度越大越好
				if(this.getPathLength() >  o.getPathLength()){
					return -1;
				}else if(this.getPathLength() <  o.getPathLength()){
					return 1;
				}else {
					//根据统计学结论,逆向切分概率高于正向切分,因此位置越靠后的优先
					if(this.pathEnd > o.pathEnd){
						return -1;
					}else if(pathEnd < o.pathEnd){
						return 1;
					}else{
						//词长越平均越好
						if(this.getXWeight() > o.getXWeight()){
							return -1;
						}else if(this.getXWeight() < o.getXWeight()){
							return 1;
						}else {
							//词元位置权重比较
							if(this.getPWeight() > o.getPWeight()){
								return -1;
							}else if(this.getPWeight() < o.getPWeight()){
								return 1;
							}
							
						}
					}
				}
			}
		}
		return 0;
	}
-----------------------------------------------------------------------
粒度细分的取舍。
              取可能出现一些确实歧义的
              不又想实现搜索出来

      6.自定义字典

 

分享到:
评论

相关推荐

    elasticsearch-analysis-ik-1.2.6

    IK分析器的设计理念是“简单、灵活、高效”,并且持续优化,不断适应中文语言的复杂性。 ### 二、IK分析器1.2.6特性 1. **词典支持**:IK分析器1.2.6包含了丰富的中文词典,涵盖了常用词汇和专有名词,同时支持...

    ik分词器tar包 7.10.2

    6. **与Elasticsearch集成**:IK分词器与Elasticsearch有良好的集成,安装简单,只需将解压后的`elasticsearch-analysis-ik-7.10.2`目录复制到Elasticsearch的`plugins`目录下即可。 **安装与使用** 1. **下载与...

    solr和IK安装包

    而IK中文分析器则是一款针对中文处理的开源分析工具,主要用于对中文文本进行分词和其他预处理操作。 首先,让我们详细了解一下Solr。Solr的核心功能在于提供高效的全文搜索能力,它通过建立倒排索引来实现快速的...

    IK.rar_ik_robot_动力学 机器人_机器人_机器人IK

    描述中提到的"本代码采用反向动力学制作一个简单机器人手臂,去够指定的点",意味着这个项目是通过编程实现的,它设计了一个简单的机器人臂模型,并使用逆运动学算法来计算出各个关节应有的角度,使得机器人臂能够...

    elasticsearch-analysis-ik-7.12.1.zip

    IK Standard 版本提供了更复杂的分词策略,而 IK Smart 版本则倾向于简单快速的分词效果。 **版本 7.12.1** IK 分词器 7.12.1 是针对 Elasticsearch 7.12.1 版本定制的,确保了与该版本的兼容性和最佳性能。这个...

    elasticsearch7.8.0版本的IK分词器

    9. **插件安装与升级**: 安装 IK 分词器非常简单,只需将 `elasticsearch-analysis-ik-7.8.0` 文件解压后放入 Elasticsearch 的 plugins 目录下,重启服务即可。如果需要升级,只需替换新的插件版本并重新启动。 在...

    使用IK Analyzer实现中文分词之Java实现

    IK Analyzer 是一个开源的,基于 java 语言开发的轻量级的中文分词工具包。从 2006年 12 月推出 1.0 版... 在 2012 版本中,IK 实现了简单的分词歧义排除算法,标志着 IK 分词器从单纯的词典分词向模拟语义分词衍化。

    elasticsearch-analysis-ik-7.12.0.zip

    《Elasticsearch IK 分析器插件7.12.0版详解》 Elasticsearch是一种流行的开源全文搜索引擎,以其强大的搜索能力和灵活的数据处理能力深受开发者喜爱。在处理中文文本时,选择合适的分析器至关重要,而`elastic...

    elasticsearch-analysis-ik-7.17.18.zip

    安装IK分析器插件非常简单,只需将elasticsearch-analysis-ik-7.17.18.jar拷贝到Elasticsearch的plugins目录下,然后重启Elasticsearch即可。在ES的配置文件`elasticsearch.yml`中,可以通过`analysis`节点来配置IK...

    elasticsearch-analysis-ik-7.5.2.7z

    Elasticsearch 分析插件 IK 是一款非常流行的中文分词插件,专为 Elasticsearch 设计。这个版本,即 "elasticsearch-analysis-ik-7.5.2",是为了与 Elasticsearch 7.5.2 版本兼容而开发的。本文将深入探讨这款插件的...

    elasticsearch-analysis-ik-7.4.2.zip

    安装 IK 分词器至 Elasticsearch 非常简单,只需将 `elasticsearch-analysis-ik-7.4.2.zip` 文件解压后,将 `plugins` 目录下的 `ik` 文件夹复制到 Elasticsearch 的 `plugins` 目录下,然后重启 Elasticsearch 即可...

    ik中文分词词库,包含不低于20万词

    6. **应用实例**:ik分词器常被用于搜索引擎的索引构建、文本情感分析、新闻摘要生成、聊天机器人对话理解等多种NLP任务中。 总之,“ik中文分词词库”是一个强大而实用的工具,对于任何需要处理中文文本的项目来说...

    elasticsearch-analysis-ik-5.6.12.zip

    这里的`my_analyzer`是用户自定义的分析器名称,`type: ik_max_word`表示使用IK分词器的最大模式。最大模式会尽可能多地对词语进行拆分,而智能模式则更倾向于拆分成常用词汇。 通过以上配置,Elasticsearch在索引...

    elasticsearch-IK-analysis-6.5.0-windows

    总结来说,"elasticsearch-IK-analysis-6.5.0-windows"是用于Windows环境下安装和使用IK分析器的资源包,通过简单的步骤可以将强大的中文分词功能集成到Elasticsearch中,从而提升中文搜索的准确性和效率。...

    elasticsearch-analysis-ik-6.8.0.zip

    Elasticsearch 分析插件 IK 分词器是用于优化 Elasticsearch 搜索引擎中文处理的重要工具。在中文环境下,正确的分词对于提升搜索质量和精确性至关重要。IK 分词器因其高效的性能和丰富的自定义功能,在 Elastic...

    elasticsearch-analysis-ik-5.6.8 ik分词器插件

    在实际应用中,我们还需要在Elasticsearch的配置文件(如elasticsearch.yml)中指定使用IK分词器,或者在创建索引时动态设置分析器。例如: ```yaml analysis: analyzer: my_analyzer: type: "ik_max_word" # ...

    IKAnalyzer

    IKAnalyzer,全称为“Intelligent Chinese Analyzer”,是由刘海洋发起的开源项目,最初设计目标是为了提供一个简单、高效、可扩展的中文分词解决方案。该工具主要基于Java语言开发,支持Lucene、Solr等全文检索框架...

    IK Analyzer.zip

    IK Analyzer 是一个开源的,基于java语言开发的轻量级的中文分词工具包。...在2012版本中,IK实现了简单的分词歧义排除算法,标志着IK分词器从单纯的词典分词向模拟语义分词衍化。此版本针对Lucene8.6.3进行修改

    IKAnalyzer-2012_u7.jar

    ****关键内容: 此中文分词器基于lucene5.5.5和jdk1.7打包发布***** IK Analyzer 是一个开源的,基于java语言...在2012版本中,IK实现了简单的分词歧义排除算法,标志着IK分词器从单纯的词典分词向模拟语义分词衍化。

    maven 构建好的 IK 分词器,可直接使用

    **标题解析:** ...总结,这个压缩包提供的是一款已经构建完成的 IK 分词器,适用于 Elasticsearch 2.2 版本,用户可以简单地将其安装在 Elasticsearch 的插件目录下,从而提升对中文内容的搜索和索引能力。

Global site tag (gtag.js) - Google Analytics