如何解决在Apache的Lucene中使用默认和自定义停用词奇怪的输出
我正在使用Apache的Lucene(8.6.3)和以下Java 8代码从字符串中删除停用词:
private static final String CONTENTS = "contents";
final String text = "This is a short test! Bla!";
final List<String> stopWords = Arrays.asList("short","test");
final CharArraySet stopSet = new CharArraySet(stopWords,true);
try {
Analyzer analyzer = new StandardAnalyzer(stopSet);
TokenStream tokenStream = analyzer.tokenStream(CONTENTS,new StringReader(text));
CharTermAttribute term = tokenStream.addAttribute(CharTermAttribute.class);
tokenStream.reset();
while(tokenStream.incrementToken()) {
System.out.print("[" + term.toString() + "] ");
}
tokenStream.close();
analyzer.close();
} catch (IOException e) {
System.out.println("Exception:\n");
e.printStackTrace();
}
这将输出所需的结果:
[此] [是] [a] [bla]
现在,我要使用默认的英语停止设置,该设置也应删除“ this”,“ is”和“ a”(根据github)和上面的自定义停止设置(实际是我'将要使用更长的时间),所以我尝试了这一点:
Analyzer analyzer = new EnglishAnalyzer(stopSet);
输出为:
[thi] [是] [a] [bla]
是的,“ this”中的“ s”丢失了。是什么原因造成的?它也没有使用默认的停止设置。
以下更改删除了默认停用词和自定义停用词:
Analyzer analyzer = new EnglishAnalyzer();
TokenStream tokenStream = analyzer.tokenStream(CONTENTS,new StringReader(text));
tokenStream = new StopFilter(tokenStream,stopSet);
问题:执行此操作的“正确”方法是什么?本身使用tokenStream
(请参见上面的代码)会引起问题吗?
奖励问题:如何输出其余单词的大小写正确,从而在原始文本中使用它们?
解决方法
我将分两部分解决这个问题:
- 停用词
- 保留原始案例
处理组合停用词
要处理Lucene的英语停用词列表和您自己的自定义列表的组合,可以按以下方式创建合并列表:
import org.apache.lucene.analysis.en.EnglishAnalyzer;
...
final List<String> stopWords = Arrays.asList("short","test");
final CharArraySet stopSet = new CharArraySet(stopWords,true);
CharArraySet enStopSet = EnglishAnalyzer.ENGLISH_STOP_WORDS_SET;
stopSet.addAll(enStopSet);
上面的代码只是采用与Lucene捆绑在一起的英语停用词,然后与您的列表合并。
给出以下输出:
[bla]
处理单词大小写
这涉及更多。正如您所注意到的,StandardAnalyzer
包括将所有单词都转换为小写的步骤-因此我们不能使用它。
此外,如果您想维护自己的自定义停用词列表,并且该列表有任何大小,我建议您将其存储在自己的文本文件中,而不是将列表嵌入到您的代码中。
因此,假设您有一个名为stopwords.txt
的文件。在此文件中,每行将只有一个单词-并且该文件已经包含您的自定义停用词和英语停用词的官方列表的合并列表。
您将需要自己手动准备此文件(即,忽略此答案第1部分中的注释)。
我的测试文件就是这样:
short
this
is
a
test
the
him
it
我也更喜欢将CustomAnalyzer
用于这样的事情,因为它使我可以非常简单地构建分析器。
import org.apache.lucene.analysis.custom.CustomAnalyzer;
...
Analyzer analyzer = CustomAnalyzer.builder()
.withTokenizer("icu")
.addTokenFilter("stop","ignoreCase","true","words","stopwords.txt","format","wordset")
.build();
这将执行以下操作:
-
它使用“ icu”令牌生成器
org.apache.lucene.analysis.icu.segmentation.ICUTokenizer
,该令牌生成器负责对Unicode空格进行令牌化以及处理标点符号。 -
它将应用停用词列表。请注意,
true
属性使用了ignoreCase
,并引用了停用词文件。wordset
的格式表示“每行一个单词”(也有其他格式)。
关键是上面的链中没有任何东西可以改变单词大小写。
因此,现在,使用这个新的分析器,输出如下:
[Bla]
最终提示
您将停止清单文件放在哪里?默认情况下,Lucene希望在您的应用程序的类路径中找到它。因此,例如,您可以将其放在默认程序包中。
但是请记住,该文件需要由您的构建过程来处理,以便它与应用程序的类文件(而不是源代码)一起出现。
我主要使用Maven-因此,我将其保存在POM中以确保根据需要部署“ .txt”文件:
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
</build>
这告诉Maven将文件(Java源文件除外)复制到构建目标-从而确保文本文件被复制。
最后的注释-我没有调查您为什么得到那个被截断的[thi]
令牌。如果有机会,我会仔细看看。
后续问题
合并后,我必须使用StandardAnalyzer,对吗?
是的,这是正确的。我在答案的第1部分中提供的注释直接与您问题中的代码以及您使用的StandardAnalyzer相关。
我想将停用词文件保留在特定的未导入路径上-怎么做?
您可以告诉CustomAnalyzer在“资源”目录中查找停用词文件。该目录可以位于文件系统上的任何位置(如您所述,为便于维护):
import java.nio.file.Path;
import java.nio.file.Paths;
...
Path resources = Paths.get("/path/to/resources/directory");
Analyzer analyzer = CustomAnalyzer.builder(resources)
.withTokenizer("icu")
.addTokenFilter("stop","wordset")
.build();
我们现在使用.builder()
代替了.builder(resources)
。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。