Вхождения подстроки в строку

122

Почему у меня не останавливается следующий алгоритм? (str - это строка, в которой я ищу, findStr - это строка, которую я пытаюсь найти)

String str = "helloslkhellodjladfjhello";
String findStr = "hello";
int lastIndex = 0;
int count = 0;

while (lastIndex != -1) {
    lastIndex = str.indexOf(findStr,lastIndex);

    if( lastIndex != -1)
        count++;

    lastIndex += findStr.length();
}

System.out.println(count);
Роберт Харви
источник
8
У нас получилось действительно хорошо в Udacity: мы использовали newSTR = str.replace (findStr, ""); и вернул count = ((str.length () - newSTR.length ()) / findStr.length ());
SolarLunix
Аналогичный вопрос для персонажей: stackoverflow.com/q/275944/873282
koppor
Разве вы не хотите также учитывать тот случай, когда префикс строки поиска является ее суффиксом? В этом случае я не думаю, что какой-либо из предложенных ответов сработает. вот пример. В этом случае вам понадобится более сложный алгоритм, такой как Knuth Morris Pratt (KMP), который закодирован в книге CLRS
Сид
он не останавливается для вас, потому что после достижения вашего условия остановки (lastIndex == -1) вы сбрасываете его, увеличивая значение lastIndex (lastIndex + = findStr.length ();)
Legna

Ответы:

84

Последняя строка создавала проблему. lastIndexникогда не будет на -1, поэтому будет бесконечный цикл. Это можно исправить, переместив последнюю строку кода в блок if.

String str = "helloslkhellodjladfjhello";
String findStr = "hello";
int lastIndex = 0;
int count = 0;

while(lastIndex != -1){

    lastIndex = str.indexOf(findStr,lastIndex);

    if(lastIndex != -1){
        count ++;
        lastIndex += findStr.length();
    }
}
System.out.println(count);
codebreach
источник
121
Этот ответ является точной копией сообщения, которое я написал час назад;)
Оливье
8
Обратите внимание, что это может вернуть или не вернуть ожидаемый результат. С подстрокой «aa» и строкой для поиска «aaa» ожидаемое количество вхождений может быть равно одному (возвращается этим кодом), но также может быть два (в этом случае вам понадобится «lastIndex ++» вместо «lastIndex + = findStr.length () ") в зависимости от того, что вы ищете.
Станислав Князев
@olivier не видел этого ... :( @stan это абсолютно правильно ... я просто исправлял код в проблеме ... думаю, это зависит от того, что означает bobcom по количеству вхождений в строку ...
codebreach
1
Когда люди научатся оборачивать подобные вещи в статический метод копирования и вставки? Смотрите мой ответ ниже, он также более оптимизирован.
ммм
1
Мораль здесь заключается в том, что если вы собираетесь написать ответ, сначала проверьте, написал ли уже кто-то такой же ответ. На самом деле нет никакой пользы в том, что один и тот же ответ появляется дважды, независимо от того, был ли ваш ответ скопирован или написан независимо.
Давуд ибн Карим
193

Как насчет использования StringUtils.countMatches из Apache Commons Lang?

String str = "helloslkhellodjladfjhello";
String findStr = "hello";

System.out.println(StringUtils.countMatches(str, findStr));

Это выводит:

3
A_M
источник
9
Каким бы правильным ни было это предложение, его нельзя принять в качестве решения, поскольку оно не отвечает на вопрос ОП
kommradHomer
3
Это устарело или что-то в этом роде .. моя IDE не распознает
Вамси Паван Махеш
@VamsiPavanMahesh StringUtils - это библиотека Apache Commons. Проверьте здесь: commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/…
Anup
Этот ответ является копией ответа Питера Лоури днем ​​ранее (см. Ниже).
Зон
StringUtilsне имеет countMatchesметода.
клетчатая
117

Ваш lastIndex += findStr.length();был помещен за скобки, что привело к бесконечному циклу (когда вхождения не было найдено, lastIndex всегда был findStr.length()).

Вот исправленная версия:

String str = "helloslkhellodjladfjhello";
String findStr = "hello";
int lastIndex = 0;
int count = 0;

while (lastIndex != -1) {

    lastIndex = str.indexOf(findStr, lastIndex);

    if (lastIndex != -1) {
        count++;
        lastIndex += findStr.length();
    }
}
System.out.println(count);
Оливье
источник
92

Укороченная версия. ;)

String str = "helloslkhellodjladfjhello";
String findStr = "hello";
System.out.println(str.split(findStr, -1).length-1);
Питер Лоури
источник
8
return haystack.split(Pattern.quote(needle), -1).length - 1;если напримерneedle=":)"
Mr_and_Mrs_D
2
@lOranger Без ,-1него будут отбрасываться конечные совпадения.
Питер Лоури
3
Ой, спасибо, приятно знать! Это научит меня читать маленькие строчки в javadoc ...
Лоран Грегуар
4
Ницца! Но он включает только неперекрывающиеся совпадения, не так ли? Например, соответствие «aa» в «aaa» вернет 1, а не 2? Конечно, включение совпадающих или неперекрывающихся совпадений является действительным и зависит от требований пользователя (возможно, флаг для индикации совпадений количества, да / нет)?
Корнел Массон
2
-1 .. попробуйте запустить это на "aaaa" и "aa" .. правильный ответ - 3, а не 2.
Kalyanaraman Santhanam
79

Вам действительно нужно самому заниматься подбором? Регулярные выражения выглядят аккуратнее, особенно если все, что вам нужно, - это количество вхождений:

String str = "helloslkhellodjladfjhello";
Pattern p = Pattern.compile("hello");
Matcher m = p.matcher(str);
int count = 0;
while (m.find()){
    count +=1;
}
System.out.println(count);     
джинсовый
источник
1
Он НЕ находит специальные символы, он найдет 0 для строк ниже: String str = "hel+loslkhel+lodjladfjhel+lo"; Pattern p = Pattern.compile("hel+lo");
Бен
13
да, будет, если вы правильно выразите свое регулярное выражение. попробуйте Pattern.compile("hel\\+lo");в +знак имеет особый смысл в регулярных выражениях и должен быть экранирован.
Жан
4
Если вы хотите взять произвольную строку и использовать ее как точное соответствие, игнорируя все специальные символы регулярных выражений, то Pattern.quote(str)ваш друг!
Майк Фуртак
2
это не работает для "aaa", если str = "aaaaaa". Есть 4 ответа, но ваш дает 2
Пуджан Шривастава
Это решение не работает для этого случая: str = "Это тестовая \\ n \\ r строка", subStr = "\\ r", показывает 0 вхождений.
Максим Овсяников 01
19

Я очень удивлен, что никто не упомянул об этом одном лайнере. Это просто, лаконично и работает немного лучше, чемstr.split(target, -1).length-1

public static int count(String str, String target) {
    return (str.length() - str.replace(target, "").length()) / target.length();
}
kmecpp
источник
Должен быть главный ответ. Спасибо!
lakam99
12

Вот он, завернутый в красивый и многоразовый метод:

public static int count(String text, String find) {
        int index = 0, count = 0, length = find.length();
        while( (index = text.indexOf(find, index)) != -1 ) {                
                index += length; count++;
        }
        return count;
}
ммм
источник
8
String str = "helloslkhellodjladfjhello";
String findStr = "hello";
int lastIndex = 0;
int count = 0;

while((lastIndex = str.indexOf(findStr, lastIndex)) != -1) {
     count++;
     lastIndex += findStr.length() - 1;
}
System.out.println(count);

в конце цикла счетчик равен 3; Надеюсь, поможет

DFA
источник
5
Код содержит ошибку. Если мы ищем один символ, findStr.length() - 1возвращается 0, и мы находимся в бесконечном цикле.
Ян Боднар
6

Многие из приведенных ответов не соответствуют одному или нескольким из:

  • Выкройки произвольной длины
  • Перекрывающиеся совпадения (например, счет «232» в «23232» или «аа» в «ааа»)
  • Мета-символы регулярного выражения

Вот что я написал:

static int countMatches(Pattern pattern, String string)
{
    Matcher matcher = pattern.matcher(string);

    int count = 0;
    int pos = 0;
    while (matcher.find(pos))
    {
        count++;
        pos = matcher.start() + 1;
    }

    return count;
}

Пример вызова:

Pattern pattern = Pattern.compile("232");
int count = countMatches(pattern, "23232"); // Returns 2

Если вам нужен поиск не по регулярному выражению, просто скомпилируйте свой шаблон соответствующим образом с LITERALфлагом:

Pattern pattern = Pattern.compile("1+1", Pattern.LITERAL);
int count = countMatches(pattern, "1+1+1"); // Returns 2
benkc
источник
Да ... удивлен, что в Apache StringUtils нет ничего подобного.
Майк грызун
6
public int countOfOccurrences(String str, String subStr) {
  return (str.length() - str.replaceAll(Pattern.quote(subStr), "").length()) / subStr.length();
}
Максим Овсяников
источник
Хороший ответ. Не могли бы вы добавить несколько заметок о том, как это работает?
santhosh kumar
Конечно, str - это наша исходная строка, subStr - это подстрока. Цель состоит в том, чтобы вычислить количество вхождений subStr в str. Для этого воспользуемся формулой: (ab) / c, где a - длина str, b - длина str без всех вхождений subStr (для этого мы удаляем все вхождения subStr из str), c - длина subStr , Итак, в основном мы извлекаем из длины str - длину str без всех subStr, а затем делим результат на длину subStr. Пожалуйста, дайте мне знать, если у вас возникнут другие вопросы.
Максим Овсяников
Сантош, добро пожаловать! Важной частью является использование Pattern.quote для subStr, в противном случае в некоторых случаях может произойти сбой, например: str = "Это тестовая \\ n \\ r строка", subStr = "\\ r". Некоторые аналогичные ответы, представленные здесь, не используют Pattern, поэтому в таких случаях они не работают.
Максим Овсяников 01
Нет причин для регулярного выражения, использовать replace, нет replaceAll.
NateS
3

Увеличивайте lastIndexкаждый раз, когда вы ищете следующее вхождение.

В противном случае он всегда находит первую подстроку (в позиции 0).

Станислав Князев
источник
3
public int indexOf(int ch,
                   int fromIndex)

Возвращает индекс в этой строке первого вхождения указанного символа, начиная поиск с указанного индекса.

Таким образом, ваше lastindexзначение всегда равно 0, и оно всегда находит приветствие в строке.

Бхушан Бхангале
источник
2

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

str.split(findStr).length

Он не отбрасывает конечные совпадения, используя пример в вопросе.

отметка
источник
1
Это уже было рассмотрено в другом ответе ; и этот ответ тоже помог.
michaelb958 - GoFundMonica
1
Это должен быть комментарий к рассматриваемому ответу, а не другой ответ.
james.garriss
2

Вы можете количество вхождений, используя встроенную библиотечную функцию:

import org.springframework.util.StringUtils;
StringUtils.countOccurrencesOf(result, "R-")
Виктор
источник
1
Не работает, следует указать используемую зависимость.
Saikat
1

попробуйте добавить lastIndex+=findStr.length()в конец цикла, иначе вы попадете в бесконечный цикл, потому что, найдя подстроку, вы снова и снова пытаетесь найти ее с той же последней позиции.

Торстен Шлейнцер
источник
1

Попробуй это. Он заменяет все совпадения на -.

String str = "helloslkhellodjladfjhello";
String findStr = "hello";
int numberOfMatches = 0;
while (str.contains(findStr)){
    str = str.replaceFirst(findStr, "-");
    numberOfMatches++;
}

И если вы не хотите разрушать свой, strвы можете создать новую строку с тем же содержанием:

String str = "helloslkhellodjladfjhello";
String strDestroy = str;
String findStr = "hello";
int numberOfMatches = 0;
while (strDestroy.contains(findStr)){
    strDestroy = strDestroy.replaceFirst(findStr, "-");
    numberOfMatches++;
}

После выполнения этого блока это будут ваши значения:

str = "helloslkhellodjladfjhello"
strDestroy = "-slk-djladfj-"
findStr = "hello"
numberOfMatches = 3
Xander
источник
1

Как предложил @Mr_and_Mrs_D:

String haystack = "hellolovelyworld";
String needle = "lo";
return haystack.split(Pattern.quote(needle), -1).length - 1;
Рон Теслер
источник
1

Основываясь на существующих ответах, я хотел бы добавить «более короткую» версию без if:

String str = "helloslkhellodjladfjhello";
String findStr = "hello";

int count = 0, lastIndex = 0;
while((lastIndex = str.indexOf(findStr, lastIndex)) != -1) {
    lastIndex += findStr.length() - 1;
    count++;
}

System.out.println(count); // output: 3
sjkm
источник
это учитывает, повторяется ли строка, например, если вы ищете строку «xx» в строке «xxx».
tCoe
1

Вот расширенная версия для подсчета того, сколько раз токен встречался в строке, введенной пользователем:

public class StringIndexOf {

    public static void main(String[] args) {

        Scanner scanner = new Scanner(System.in);

        System.out.println("Enter a sentence please: \n");
        String string = scanner.nextLine();

        int atIndex = 0;
        int count = 0;

        while (atIndex != -1)
        {
            atIndex = string.indexOf("hello", atIndex);

            if(atIndex != -1)
            {
                count++;
                atIndex += 5;
            }
        }

        System.out.println(count);
    }

}
Venzentx
источник
1

Этот метод ниже показывает, сколько раз подстрока повторяется на всей строке ur. Надеюсь, вам понадобится: -

    String searchPattern="aaa"; // search string
    String str="aaaaaababaaaaaa"; // whole string
    int searchLength = searchPattern.length(); 
    int totalLength = str.length(); 
    int k = 0;
    for (int i = 0; i < totalLength - searchLength + 1; i++) {
        String subStr = str.substring(i, searchLength + i);
        if (subStr.equals(searchPattern)) {
           k++;
        }

    }
Дуггу
источник
0

вот другое решение без использования регулярных выражений / шаблонов / сопоставителей или даже без использования StringUtils.

String str = "helloslkhellodjladfjhelloarunkumarhelloasdhelloaruhelloasrhello";
        String findStr = "hello";
        int count =0;
        int findStrLength = findStr.length();
        for(int i=0;i<str.length();i++){
            if(findStr.startsWith(Character.toString(str.charAt(i)))){
                if(str.substring(i).length() >= findStrLength){
                    if(str.substring(i, i+findStrLength).equals(findStr)){
                        count++;
                    }
                }
            }
        }
        System.out.println(count);
Арун Кумар Мудрабойна
источник
0

Если вам нужен индекс каждой подстроки в исходной строке, вы можете сделать что-нибудь с indexOf следующим образом:

 private static List<Integer> getAllIndexesOfSubstringInString(String fullString, String substring) {
    int pointIndex = 0;
    List<Integer> allOccurences = new ArrayList<Integer>();
    while(fullPdfText.indexOf(substring,pointIndex) >= 0){
       allOccurences.add(fullPdfText.indexOf(substring, pointIndex));
       pointIndex = fullPdfText.indexOf(substring, pointIndex) + substring.length();
    }
    return allOccurences;
}
носорог
источник
0
public static int getCountSubString(String str , String sub){
int n = 0, m = 0, counter = 0, counterSub = 0;
while(n < str.length()){
  counter = 0;
  m = 0;
  while(m < sub.length() && str.charAt(n) == sub.charAt(m)){
    counter++;
    m++; n++;
  }
  if (counter == sub.length()){
    counterSub++;
    continue;
  }
  else if(counter > 0){
    continue;
  }
  n++;
}

return  counterSub;

}

Николай Нечай
источник
этому вопросу 8 лет, и без каких-либо указаний на то, почему это решение лучше, чем 22 других опубликованных решения, его, вероятно, следует удалить
Джейсон Уиллер
0

Это решение выводит на печать общее количество вхождений данной подстроки по всей строке, а также включает случаи, когда существуют перекрывающиеся совпадения.

class SubstringMatch{
    public static void main(String []args){
        //String str = "aaaaabaabdcaa";
        //String sub = "aa";
        //String str = "caaab";
        //String sub = "aa";
        String str="abababababaabb";
        String sub = "bab";

        int n = str.length();
        int m = sub.length();

        // index=-1 in case of no match, otherwise >=0(first match position)
        int index=str.indexOf(sub), i=index+1, count=(index>=0)?1:0;
        System.out.println(i+" "+index+" "+count);

        // i will traverse up to only (m-n) position
        while(index!=-1 && i<=(n-m)){   
            index=str.substring(i, n).indexOf(sub);
            count=(index>=0)?count+1:count;
            i=i+index+1;  
            System.out.println(i+" "+index);
        }
        System.out.println("count: "+count);
    }
}
Анубхав Сингх
источник