/**
* Copyright (c)2010-2011 Enterprise Website Content Management System(EWCMS), All rights reserved.
* EWCMS PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
* http://www.ewcms.com
*/
package com.ewcms.content.document.util.analyzer;
import java.io.IOException;
import java.io.Reader;
import java.util.List;
import com.ewcms.content.document.util.analyzer.cfg.Configuration;
import com.ewcms.content.document.util.analyzer.help.CharacterHelper;
import com.ewcms.content.document.util.analyzer.seg.ISegmenter;
/**
* <ul>
* IK主分词器<br/>
* 注:IKSegmentation是一个lucene无关的通用分词器
*
* @author 吴智俊
*/
public final class IKSegmentation{
private Reader input;
//默认缓冲区大小
private static final int BUFF_SIZE = 3072;
//缓冲区耗尽的临界值
private static final int BUFF_EXHAUST_CRITICAL = 48;
//字符窜读取缓冲
private char[] segmentBuff;
//分词器上下文
private Context context;
//分词处理器列表
private List<ISegmenter> segmenters;
/**
* IK主分词器构造函数
* 默认最细粒度切分
* @param input
*/
public IKSegmentation(Reader input){
this(input , false);
}
/**
* IK主分词器构造函数
* @param input
* @param isMaxWordLength 当为true时,分词器进行最大词长切分
*/
public IKSegmentation(Reader input , boolean isMaxWordLength){
this.input = input ;
segmentBuff = new char[BUFF_SIZE];
context = new Context(segmentBuff , isMaxWordLength);
segmenters = Configuration.loadSegmenter();
}
/**
* 获取下一个语义单元
* @return 没有更多的词元,则返回null
* @throws IOException
*/
public synchronized Lexeme next() throws IOException {
if(context.getResultSize() == 0){
/*
* 从reader中读取数据,填充buffer
* 如果reader是分次读入buffer的,那么buffer要进行移位处理
* 移位处理上次读入的但未处理的数据
*/
int available = fillBuffer(input);
if(available <= 0){
context.resetContext();
return null;
}else{
//分词处理
int analyzedLength = 0;
for(int buffIndex = 0 ; buffIndex < available ; buffIndex++){
//移动缓冲区指针
context.setCursor(buffIndex);
//进行字符规格化(全角转半角,大写转小写处理)
segmentBuff[buffIndex] = CharacterHelper.regularize(segmentBuff[buffIndex]);
//遍历子分词器
for(ISegmenter segmenter : segmenters){
segmenter.nextLexeme(segmentBuff , context);
}
analyzedLength++;
/*
* 满足一下条件时,
* 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.reset();
}
//记录最近一次分析的字符长度
context.setLastAnalyzed(analyzedLength);
//同时累计已分析的字符长度
context.setBuffOffset(context.getBuffOffset() + analyzedLength);
//如果使用最大切分,则过滤交叠的短词元
if(context.isMaxWordLength()){
context.excludeOverlap();
}
//读取词元池中的词元
return buildLexeme(context.firstLexeme());
}
}else{
//读取词元池中的已有词元
return buildLexeme(context.firstLexeme());
}
}
/**
* 根据context的上下文情况,填充segmentBuff
* @param reader
* @return 返回待分析的(有效的)字串长度
* @throws IOException
*/
private int fillBuffer(Reader reader) throws IOException{
int readCount = 0;
if(context.getBuffOffset() == 0){
//首次读取reader
readCount = reader.read(segmentBuff);
}else{
int offset = context.getAvailable() - context.getLastAnalyzed();
if(offset > 0){
//最近一次读取的>最近一次处理的,将未处理的字串拷贝到segmentBuff头部
System.arraycopy(segmentBuff , context.getLastAnalyzed() , this.segmentBuff , 0 , offset);
readCount = offset;
}
//继续读取reader ,以onceReadIn - onceAnalyzed为起始位置,继续填充segmentBuff剩余的部分
readCount += reader.read(segmentBuff , offset , BUFF_SIZE - offset);
}
//记录最后一次从Reader中读入的可用字符长度
context.setAvailable(readCount);
return readCount;
}
/**
* 取出词元集合中的下一个词元
* @return Lexeme
*/
private Lexeme buildLexeme(Lexeme lexeme){
if(lexeme != null){
//生成lexeme的词元文本
lexeme.setLexemeText(String.valueOf(segmentBuff , lexeme.getBegin() , lexeme.getLength()));
return lexeme;
}else{
return null;
}
}
/**
* 重置分词器到初始状态
* @param input
*/
public synchronized void reset(Reader input) {
this.input = input;
context.resetContext();
for(ISegmenter segmenter : segmenters){
segmenter.reset();
}
}
}