Перейти к основному содержимому

Удаление стоп-слов из строки в Java

· 3 мин. чтения

1. Обзор

В этом руководстве мы обсудим различные способы удаления стоп-слов из строки в Java. Это полезная операция в тех случаях, когда мы хотим удалить нежелательные или запрещенные слова из текста, например комментарии или обзоры, добавленные пользователями онлайн-сайта.

Мы будем использовать простой цикл, Collection.removeAll() и регулярные выражения.

Наконец, мы сравним их производительность с помощью Java Microbenchmark Harness .

2. Загрузка стоп-слов

Сначала мы загрузим стоп-слова из текстового файла.

Здесь у нас есть файл english_stopwords.txt , который содержит список слов, которые мы считаем стоп-словами, таких как я , он , она и .

Мы загрузим стоп-слова в список строк , используя Files.readAllLines () :

@BeforeClass
public static void loadStopwords() throws IOException {
stopwords = Files.readAllLines(Paths.get("english_stopwords.txt"));
}

3. Удаление стоп-слов вручную

Для нашего первого решения мы удалим стоп-слова вручную, перебирая каждое слово и проверяя, является ли оно стоп-словом :

@Test
public void whenRemoveStopwordsManually_thenSuccess() {
String original = "The quick brown fox jumps over the lazy dog";
String target = "quick brown fox jumps lazy dog";
String[] allWords = original.toLowerCase().split(" ");

StringBuilder builder = new StringBuilder();
for(String word : allWords) {
if(!stopwords.contains(word)) {
builder.append(word);
builder.append(' ');
}
}

String result = builder.toString().trim();
assertEquals(result, target);
}

4. Использование Collection.removeAll()

Далее, вместо перебора каждого слова в нашей строке , мы можем использовать Collection.removeAll() для удаления всех стоп-слов сразу :

@Test
public void whenRemoveStopwordsUsingRemoveAll_thenSuccess() {
ArrayList<String> allWords =
Stream.of(original.toLowerCase().split(" "))
.collect(Collectors.toCollection(ArrayList<String>::new));
allWords.removeAll(stopwords);

String result = allWords.stream().collect(Collectors.joining(" "));
assertEquals(result, target);
}

В этом примере после разделения нашей строки на массив слов мы преобразуем ее в ArrayList , чтобы можно было применить метод removeAll() .

5. Использование регулярных выражений

Наконец, мы можем создать регулярное выражение из нашего списка стоп-слов , а затем использовать его для замены стоп-слов в нашей строке :

@Test
public void whenRemoveStopwordsUsingRegex_thenSuccess() {
String stopwordsRegex = stopwords.stream()
.collect(Collectors.joining("|", "\\b(", ")\\b\\s?"));

String result = original.toLowerCase().replaceAll(stopwordsRegex, "");
assertEquals(result, target);
}

Результирующее выражение stopwordsRegex будет иметь формат «\b(он|она|the|…)\b\s?». В этом регулярном выражении «\b» относится к границе слова, чтобы избежать замены, например, «he» в «heat», а «\s?» относится к нулю или одному пробелу, чтобы удалить лишний пробел после замены стоп-слова.

6. Сравнение производительности

Теперь давайте посмотрим, какой метод имеет лучшую производительность.

Во- первых, давайте настроим наш бенчмарк . Мы будем использовать довольно большой текстовый файл в качестве источника нашей строки с именем Shakespeare-hamlet.txt :

@Setup
public void setup() throws IOException {
data = new String(Files.readAllBytes(Paths.get("shakespeare-hamlet.txt")));
data = data.toLowerCase();
stopwords = Files.readAllLines(Paths.get("english_stopwords.txt"));
stopwordsRegex = stopwords.stream().collect(Collectors.joining("|", "\\b(", ")\\b\\s?"));
}

Затем у нас будут методы тестирования, начиная с removeManually() :

@Benchmark
public String removeManually() {
String[] allWords = data.split(" ");
StringBuilder builder = new StringBuilder();
for(String word : allWords) {
if(!stopwords.contains(word)) {
builder.append(word);
builder.append(' ');
}
}
return builder.toString().trim();
}

Далее у нас есть тест removeAll() :

@Benchmark
public String removeAll() {
ArrayList<String> allWords =
Stream.of(data.split(" "))
.collect(Collectors.toCollection(ArrayList<String>::new));
allWords.removeAll(stopwords);
return allWords.stream().collect(Collectors.joining(" "));
}

Наконец, мы добавим тест для replaceRegex() :

@Benchmark
public String replaceRegex() {
return data.replaceAll(stopwordsRegex, "");
}

И вот результат нашего теста:

Benchmark                           Mode  Cnt   Score    Error  Units
removeAll avgt 60 7.782 ± 0.076 ms/op
removeManually avgt 60 8.186 ± 0.348 ms/op
replaceRegex avgt 60 42.035 ± 1.098 ms/op

Кажется, что использование Collection.removeAll() имеет самое быстрое время выполнения, а использование регулярных выражений — самое медленное .

7. Заключение

В этой быстрой статье мы узнали о различных методах удаления стоп-слов из строки в Java. Мы также сравнили их, чтобы увидеть, какой метод имеет наилучшую производительность.

Полный исходный код примеров доступен на GitHub .