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

У меня есть строка с большим количеством слов, и у меня есть текстовый файл, содержащий несколько стоп-слов, которые мне нужно удалить из моей строки. Допустим, у меня есть строка

s="I love this phone, its super fast and there's so much new and cool things with jelly bean....but of recently I've seen some bugs."

После удаления стоп-слов строка должна выглядеть так:

"love phone, super fast much cool jelly bean....but recently bugs."

Мне удалось добиться этого, но проблема, с которой я столкнулся, заключается в том, что всякий раз, когда в строке есть соседние стоп-слова, она удаляет только первое, и я получаю результат как:

"love phone, super fast there's much and cool with jelly bean....but recently seen bugs"  

Вот мой файл stopwordslist.txt: Стоп-слова

Как я могу решить эту проблему. Вот что я сделал до сих пор:

int k=0,i,j;
ArrayList<String> wordsList = new ArrayList<String>();
String sCurrentLine;
String[] stopwords = new String[2000];
try{
        FileReader fr=new FileReader("F:\\stopwordslist.txt");
        BufferedReader br= new BufferedReader(fr);
        while ((sCurrentLine = br.readLine()) != null){
            stopwords[k]=sCurrentLine;
            k++;
        }
        String s="I love this phone, its super fast and there's so much new and cool things with jelly bean....but of recently I've seen some bugs.";
        StringBuilder builder = new StringBuilder(s);
        String[] words = builder.toString().split("\\s");
        for (String word : words){
            wordsList.add(word);
        }
        for(int ii = 0; ii < wordsList.size(); ii++){
            for(int jj = 0; jj < k; jj++){
                if(stopwords[jj].contains(wordsList.get(ii).toLowerCase())){
                    wordsList.remove(ii);
                    break;
                }
             }
        }
        for (String str : wordsList){
            System.out.print(str+" ");
        }   
    }catch(Exception ex){
        System.out.println(ex);
    }

person JavaLearner    schedule 29.12.2014    source источник
comment
сначала поможет разделение строки? что-то вроде фразы.split(разделители); вы можете отфильтровать ненужные части, прежде чем сшивать их снова. это может решить вашу проблему Это и его.   -  person Angel Koh    schedule 29.12.2014
comment
Более конкретный вопрос здесь   -  person Jitendra    schedule 22.01.2017


Ответы (10)


Ошибка заключается в том, что вы удаляете элемент из списка, который вы повторяете. Допустим, у вас есть wordsList, который содержит |word0|word1|word2|. Если ii равно 1 и проверка истинна, то вы вызываете wordsList.remove(1);. После этого ваш список |word0|word2|. Затем ii увеличивается и становится равным 2, и теперь он превышает размер вашего списка, поэтому word2 никогда не будет тестироваться.

Оттуда есть несколько решений. Например, вместо удаления значений вы можете установить значение «». Или создайте специальный список результатов.

person alain.janinm    schedule 29.12.2014

Это гораздо более элегантное решение (ИМХО), использующее только регулярные выражения:

    // instead of the ".....", add all your stopwords, separated by "|"
    // "\\b" is to account for word boundaries, i.e. not replace "his" in "this"
    // the "\\s?" is to suppress optional trailing white space
    Pattern p = Pattern.compile("\\b(I|this|its.....)\\b\\s?");
    Matcher m = p.matcher("I love this phone, its super fast and there's so much new and cool things with jelly bean....but of recently I've seen some bugs.");
    String s = m.replaceAll("");
    System.out.println(s);
person geert3    schedule 29.12.2014
comment
Проблема вовсе не в операторе break. Он берет в первый цикл первое слово текста. Затем он смотрит в списке стоп-слов, если он присутствует. Если он находит слово в списке стоп-слов, он разрывает цикл поиска. Затем он берет следующее слово и снова ищет в списке стоп-слов. - person alain.janinm; 29.12.2014
comment
опять же, как и в других ответах, вы удалите стоп-слова, которые являются подстроками обычных слов. - person Michal Lozinski; 29.12.2014
comment
@ alain.janinm вы правы, думаю, я пытался быть слишком быстрым. Поскольку вы уже дали правильный ответ, я просто удалил глупое замечание из своего ответа. - person geert3; 29.12.2014
comment
@MichalLozinski прав, я обновил свой ответ, включив в него границы слов. - person geert3; 29.12.2014
comment
@geert3 Спасибо, что приняли во внимание мой комментарий;) - person alain.janinm; 29.12.2014

Попробуйте программу ниже.

String s="I love this phone, its super fast and there's so" +
            " much new and cool things with jelly bean....but of recently I've seen some bugs.";
    String[] words = s.split(" ");
    ArrayList<String> wordsList = new ArrayList<String>();
    Set<String> stopWordsSet = new HashSet<String>();
    stopWordsSet.add("I");
    stopWordsSet.add("THIS");
    stopWordsSet.add("AND");
    stopWordsSet.add("THERE'S");

    for(String word : words)
    {
        String wordCompare = word.toUpperCase();
        if(!stopWordsSet.contains(wordCompare))
        {
            wordsList.add(word);
        }
    }

    for (String str : wordsList){
        System.out.print(str+" ");
    }

ВЫВОД: обожаю телефон, он очень быстрый, так много новых крутых вещей с желейными бобами... но в последнее время я видел некоторые ошибки.

person robin    schedule 29.12.2014
comment
Хороший улов, не удаляя ненужные, а добавляя нужные! +1 - person Charlie; 29.12.2014

Вы можете использовать функцию замены всех, как это

String yourString ="I love this phone, its super fast and there's so much new and cool things with jelly bean....but of recently I've seen some bugs."
yourString=yourString.replaceAll("stop" ,"");
person Navnath Chinchore    schedule 29.12.2014

Попробуйте использовать replaceAll API строки, например:

String myString = "I love this phone, its super fast and there's so much new and cool things with jelly bean....but of recently I've seen some bugs.";
String stopWords = "I|its|with|but";
String afterStopWords = myString.replaceAll("(" + stopWords + ")\\s*", "");
System.out.println(afterStopWords);

OUTPUT: 
love this phone, super fast and there's so much new and cool things jelly bean....of recently 've seen some bugs.
person SMA    schedule 29.12.2014

Вместо этого, почему бы вам не использовать подход ниже. Будет легче читать и понимать:

for(String word : words){
    s = s.replace(word+"\\s*", "");
}
System.out.println(s);//It will print removed word string.
person Vimal Bera    schedule 29.12.2014
comment
обратите внимание, что эта реализация приведет к двум пробелам. - person Angel Koh; 29.12.2014
comment
Проблема в том, что он также удалит стоп-слова между другими словами. Как будто это уводит его от этого также. - person JavaLearner; 29.12.2014
comment
также это означает, что для больших таблиц стоп-слов это не оптимальное решение, так как вы будете перебирать все, независимо от длины вашего текста. Тем не менее, если ваш набор стоп-слов останется таким большим, чем на самом деле, это один из самых простых ответов :) - person Michal Lozinski; 29.12.2014
comment
и если стоп-слово является последним словом, в конце предложения останется пробел. - person Michal Lozinski; 29.12.2014
comment
Я думаю, что для устранения проблем в комментариях вам понадобится следующее регулярное выражение: format("( %s )|(^%s )|( %s$)", word) Затем вы должны заменить совпадения пробелами, а затем удалить все двойные пробелы. Тем не менее, со всем этим мамбо-джамбо это начинает выглядеть грязно;) - person Michal Lozinski; 29.12.2014

Вот попробуйте это следующим образом:

   String s="I love this phone, its super fast and there's so much new and cool things with jelly bean....but of recently I've seen some bugs.";
   String stopWords[]={"love","this","cool"};
   for(int i=0;i<stopWords.length;i++){
       if(s.contains(stopWords[i])){
           s=s.replaceAll(stopWords[i]+"\\s+", ""); //note this will remove spaces at the end
       }
   }
   System.out.println(s);

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

I   phone, its super fast and there's so much new and  things with jelly bean....but of recently I've seen some bugs.
person Darshan Lila    schedule 29.12.2014
comment
после цикла for s=s.replaceAll(‹два пробела›,‹один пробел›); изменить два пробела на один пробел? - person Angel Koh; 29.12.2014
comment
Кроме того, как и в случае с ответом Вимала, вы должны удалить подстроки из середины других слов (попробуйте добавить a в качестве стоп-слова;)) - person Michal Lozinski; 29.12.2014

Попробуйте сохранить стоп-слова в коллекции наборов, а затем разметить вашу строку в список. После этого вы можете просто использовать «removeAll», чтобы получить результат.

Set<String> stopwords = new Set<>()
//fill in the set with your file

String s="I love this phone, its super fast and there's so much new and cool things with jelly bean....but of recently I've seen some bugs.";
List<String> listOfStrings = asList(s.split(" "));

listOfStrings.removeAll(stopwords);
StringUtils.join(listOfStrings, " ");

Циклы for не нужны - они обычно означают проблемы.

person Michal Lozinski    schedule 29.12.2014

Кажется, что вы делаете стоп-слово, удаляется одно стоп-слово в предложении, переходите к другому стоп-слову: вам нужно удалить все стоп-слова в каждом предложении.

Вы должны попробовать изменить свой код:

Из:

for(int ii = 0; ii < wordsList.size(); ii++){
    for(int jj = 0; jj < k; jj++){
        if(stopwords[jj].contains(wordsList.get(ii).toLowerCase())){
            wordsList.remove(ii);
            break;
        }
    }
}

Что-то вроде:

for(int ii = 0; ii < wordsList.size(); ii++)
{
    for(int jj = 0; jj < k; jj++)
    {
        if(wordsList.get(ii).toLowerCase().contains(stopwords[jj])
        {
            wordsList.remove(ii);
        }
    }
}

Обратите внимание, что break удалено, а stopword.contains(word) заменено на word.contains(stopword).

person Inquisitor    schedule 13.10.2015

Недавно одному из проектов потребовалась функциональность для фильтрации остановки/основы и нецензурных слов из данного текста или файла после просмотра нескольких блогов и рецензий. создал простую библиотеку для фильтрации данных/файла и сделал ее доступной в maven. надеюсь, что это может помочь кому-то.

https://github.com/uttesh/exude

     <dependency>
        <groupId>com.uttesh</groupId>
        <artifactId>exude</artifactId>
        <version>0.0.2</version>
    </dependency>
person Uttesh Kumar    schedule 07.01.2016
comment
это глючная библиотека - person MFARID; 23.03.2016
comment
@MFARID, не могли бы вы объяснить, на каком основании эта библиотека глючит? - person Uttesh Kumar; 25.03.2016