使用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
相关产品推荐
相关产品推荐

