You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Lucene Porter Stemmer时如何获取未词干处理的原始单词

问题原因

Lucene的TokenStream链中,所有过滤器共享同一份属性实例池。你给上游original和下游stemmed添加的CharTermAttribute本质是同一个对象,PorterStemFilter在处理时会直接修改这个对象的内容,所以最终读取到的都是词干还原后的结果。

解决方法

最稳定的方案是自定义专属Attribute存储原始分词结果,在词干还原操作之前就把原始词保存下来,避免被后续过滤器修改覆盖。

步骤1:自定义原始词存储Attribute

首先定义Attribute接口和对应的实现类(Lucene要求Attribute必须配套Impl类,命名规则为接口名+Impl):

import org.apache.lucene.util.Attribute;

// 自定义Attribute接口,用于存储原始词
public interface OriginalTermAttribute extends Attribute {
    String getOriginalTerm();
    void setOriginalTerm(String term);
}

对应的实现类:

import org.apache.lucene.util.AttributeImpl;
import org.apache.lucene.util.AttributeReflector;

public class OriginalTermAttributeImpl extends AttributeImpl implements OriginalTermAttribute {
    private String originalTerm = "";

    @Override
    public String getOriginalTerm() {
        return originalTerm;
    }

    @Override
    public void setOriginalTerm(String term) {
        this.originalTerm = term == null ? "" : term;
    }

    @Override
    public void clear() {
        originalTerm = "";
    }

    @Override
    public void reflectWith(AttributeReflector reflector) {
        reflector.reflect(OriginalTermAttribute.class, "originalTerm", originalTerm);
    }

    @Override
    public void copyTo(AttributeImpl target) {
        ((OriginalTermAttribute) target).setOriginalTerm(this.originalTerm);
    }
}

步骤2:添加前置过滤器保存原始词

在PorterStemFilter之前插入自定义过滤器,把刚分词完成、还未做词干还原的原始词存入自定义Attribute:

import org.apache.lucene.analysis.TokenFilter;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;

import java.io.IOException;

public class SaveOriginalTermFilter extends TokenFilter {
    private final CharTermAttribute termAttr = addAttribute(CharTermAttribute.class);
    private final OriginalTermAttribute originalTermAttr = addAttribute(OriginalTermAttribute.class);

    protected SaveOriginalTermFilter(TokenStream input) {
        super(input);
    }

    @Override
    public boolean incrementToken() throws IOException {
        if (input.incrementToken()) {
            // 此时获取的是未做词干还原的原始分词结果,直接保存
            originalTermAttr.setOriginalTerm(termAttr.toString());
            return true;
        }
        return false;
    }
}

步骤3:调整调用代码组装TokenStream链

Analyzer analyzer = new StandardAnalyzer();
TokenStream original = analyzer.tokenStream("StandardTokenStream", new StringReader(inputText));
// 先加原始词保存过滤器,再加词干过滤器
TokenStream saveOriginalStream = new SaveOriginalTermFilter(original);
TokenStream stemmed = new PorterStemFilter(saveOriginalStream);

// 分别获取词干结果属性和原始词自定义属性
CharTermAttribute stemmedWordAttribute = stemmed.addAttribute(CharTermAttribute.class);
OriginalTermAttribute originalWordAttribute = stemmed.addAttribute(OriginalTermAttribute.class);

stemmed.reset();
while (stemmed.incrementToken()) {
    // 可同时获取词干还原结果和原始词
    System.out.println(stemmedWordAttribute + " " + originalWordAttribute.getOriginalTerm());
}
stemmed.end();
stemmed.close();

内容的提问来源于stack exchange,提问作者NickJ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 07:45:04