Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 34 additions & 4 deletions core/src/main/java/org/wltea/analyzer/core/AnalyzeContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ class AnalyzeContext {
//分词器配置项
private Configuration cfg;

//记录已被消费的最大结束位置(含被停用词过滤的词元)
//用于修复 issue#921:全部词元被停用词过滤时 end() 返回正确的 finalOffset
private int maxConsumedEndPosition;
//记录最近一次 getNextLexeme() 调用中跳过的停用词数量
private int lastSkippedCount;

public AnalyzeContext(Configuration configuration){
this.cfg = configuration;
this.segmentBuff = new char[BUFF_SIZE];
Expand Down Expand Up @@ -322,20 +328,28 @@ private void outputSingleCJK(int index){
}

/**
* 返回lexeme
*
* 返回lexeme
*
* 同时处理合并
* @return
*/
Lexeme getNextLexeme(){
//重置本次调用的停用词跳过计数
lastSkippedCount = 0;
//从结果集取出,并移除第一个Lexme
Lexeme result = this.results.pollFirst();
while(result != null){
//数量词合并
this.compound(result);
//跟踪所有被消费的词元的最大结束位置(含被停用词过滤的)
int endPos = result.getBeginPosition() + result.getLength();
if(endPos > maxConsumedEndPosition){
maxConsumedEndPosition = endPos;
}
if(Dictionary.getSingleton().isStopWord(this.segmentBuff , result.getBegin() , result.getLength())){
//是停止词继续取列表的下一个
result = this.results.pollFirst();
lastSkippedCount++;
result = this.results.pollFirst();
}else{
//不是停止词, 生成lexeme的词元文本,输出
result.setLexemeText(String.valueOf(segmentBuff , result.getBegin() , result.getLength()));
Expand All @@ -345,6 +359,20 @@ Lexeme getNextLexeme(){
return result;
}

/**
* 获取已被消费的最大结束位置(含被停用词过滤的词元)
*/
int getMaxConsumedEndPosition(){
return maxConsumedEndPosition;
}

/**
* 获取最近一次 getNextLexeme() 调用中跳过的停用词数量
*/
int getLastSkippedCount(){
return lastSkippedCount;
}

/**
* 返回末尾非CJK字符字符数目
*/
Expand All @@ -355,7 +383,7 @@ public int getLastUselessCharNum(){
/**
* 重置分词上下文状态
*/
void reset(){
void reset(){
this.buffLocker.clear();
this.orgLexemes = new QuickSortSet();
this.available =0;
Expand All @@ -365,6 +393,8 @@ void reset(){
this.results.clear();
this.segmentBuff = new char[BUFF_SIZE];
this.pathMap.clear();
this.maxConsumedEndPosition = 0;
this.lastSkippedCount = 0;
}

/**
Expand Down
30 changes: 28 additions & 2 deletions core/src/main/java/org/wltea/analyzer/core/IKSegmenter.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ public final class IKSegmenter {
//分词歧义裁决器
private IKArbitrator arbitrator;
private Configuration configuration;
//记录已被消费的最大结束位置(在 context.reset() 前保存)
private int savedMaxConsumedEndPosition;
//记录累计跳过的停用词数量(在一次 incrementToken 调用周期内)
private int savedSkippedCount;


/**
Expand Down Expand Up @@ -94,6 +98,7 @@ private List<ISegmenter> loadSegmenters(){
*/
public synchronized Lexeme next()throws IOException{
Lexeme l = null;
savedSkippedCount = 0;
while((l = context.getNextLexeme()) == null ){
/*
* 从reader中读取数据,填充buffer
Expand All @@ -103,9 +108,11 @@ public synchronized Lexeme next()throws IOException{
int available = context.fillBuffer(this.input);
if(available <= 0){
//reader已经读完
//在 reset() 前保存已消费的最大位置,供 end() 使用
savedMaxConsumedEndPosition = context.getMaxConsumedEndPosition();
context.reset();
return null;

}else{
//初始化指针
context.initCursor();
Expand All @@ -130,8 +137,9 @@ public synchronized Lexeme next()throws IOException{
//将分词结果输出到结果集,并处理未切分的单个CJK字符
context.outputToResult();
//记录本次分词的缓冲区位移
context.markBufferOffset();
context.markBufferOffset();
}
savedSkippedCount += context.getLastSkippedCount();
return l;
}

Expand All @@ -145,6 +153,8 @@ public synchronized void reset(Reader input) {
for(ISegmenter segmenter : segmenters){
segmenter.reset();
}
savedMaxConsumedEndPosition = 0;
savedSkippedCount = 0;
}

/**
Expand All @@ -153,4 +163,20 @@ public synchronized void reset(Reader input) {
public int getLastUselessCharNum() {
return this.context.getLastUselessCharNum();
}

/**
* 获取已被消费的最大结束位置(含被停用词过滤的词元)
* 用于修复 issue#921:全部词元被停用词过滤时 end() 返回正确的 finalOffset
*/
public int getSavedMaxConsumedEndPosition() {
return Math.max(savedMaxConsumedEndPosition, context.getMaxConsumedEndPosition());
}

/**
* 获取累计跳过的停用词数量
* 用于修复 issue#921:正确设置 positionIncrement
*/
public int getSavedSkippedCount() {
return savedSkippedCount;
}
}
11 changes: 7 additions & 4 deletions core/src/main/java/org/wltea/analyzer/lucene/IKTokenizer.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,12 @@ public IKTokenizer(Configuration configuration){
public boolean incrementToken() throws IOException {
//清除所有的词元属性
clearAttributes();
skippedPositions = 0;

Lexeme nextLexeme = _IKImplement.next();
if(nextLexeme != null){
posIncrAtt.setPositionIncrement(skippedPositions +1 );
//从底层获取跳过的停用词数量,正确设置 positionIncrement
skippedPositions = _IKImplement.getSavedSkippedCount();
posIncrAtt.setPositionIncrement(skippedPositions + 1);

//将Lexeme转成Attributes
//设置词元文本
Expand All @@ -98,7 +99,7 @@ public boolean incrementToken() throws IOException {
//记录分词的最后位置
endPosition = nextLexeme.getEndPosition();
//记录词元分类
typeAtt.setType(nextLexeme.getLexemeTypeString());
typeAtt.setType(nextLexeme.getLexemeTypeString());
//返会true告知还有下个词元
return true;
}
Expand All @@ -122,7 +123,9 @@ public void reset() throws IOException {
public final void end() throws IOException {
super.end();
// set final offset
int finalOffset = correctOffset(this.endPosition+ _IKImplement.getLastUselessCharNum());
//修复 issue#921:使用 max(endPosition, savedMaxConsumedEndPosition) 确保全部词元被停用词过滤时 finalOffset 仍正确
int maxEnd = Math.max(this.endPosition, _IKImplement.getSavedMaxConsumedEndPosition());
int finalOffset = correctOffset(maxEnd + _IKImplement.getLastUselessCharNum());
offsetAtt.setOffset(finalOffset, finalOffset);
posIncrAtt.setPositionIncrement(posIncrAtt.getPositionIncrement() + skippedPositions);
}
Expand Down
Loading
Loading