Ocorrências de substring em uma string

122

Por que o seguinte algoritmo não está sendo interrompido para mim? (str é a string em que estou procurando, findStr é a string que estou tentando encontrar)

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);
Robert Harvey
fonte
8
Fizemos um ótimo no Udacity: usamos newSTR = str.replace (findStr, ""); e retornou count = ((str.length () - newSTR.length ()) / findStr.length ());
SolarLunix
Pergunta semelhante para caracteres: stackoverflow.com/q/275944/873282
koppor:
Você também não deseja explicar o caso em que o prefixo da cadeia de pesquisa é o sufixo? Nesse caso, acho que nenhuma das respostas sugeridas funcionaria. aqui está um exemplo Nesse caso, você precisaria de um algoritmo mais elaborado, como o Knuth Morris Pratt (KMP) que é codificado para cima nas CLRS reservar
Sid
não está interrompendo para você, porque depois de atingir sua condição de 'interrupção' (lastIndex == -1), você a redefine, incrementando o valor de lastIndex (lastIndex + = findStr.length ();)
Legna

Respostas:

83

A última linha estava criando um problema. lastIndexnunca seria -1, então haveria um loop infinito. Isso pode ser corrigido movendo a última linha de código para o bloco 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
fonte
121
Esta resposta é a cópia exata do post que fiz uma hora antes;)
Olivier
8
Observe que isso pode ou não retornar o resultado esperado. Com a substring "aa" e a string para pesquisar "aaa", o número de ocorrências esperadas pode ser um (retornado por esse código), mas também pode ser dois (nesse caso, você precisará de "lastIndex ++" em vez de "lastIndex + = findStr.length () ") dependendo do que você está procurando.
Stanislav Kniazev
@olivier não viu isso ... :( @stan está absolutamente correto ... eu estava apenas corrigindo o código no problema ... acho que depende do que bobcom quer dizer com número de ocorrências na string ...
codebreach
1
Quando as pessoas vão aprender a agrupar coisas assim em um método estático de copiar e colar? Veja minha resposta abaixo, também é mais otimizada.
mmm
1
A moral aqui é que, se você pretende escrever uma resposta, verifique primeiro se outra pessoa já escreveu ou não a mesma resposta. Não há realmente nenhum benefício em ter a mesma resposta exibida duas vezes, independentemente de sua resposta ter sido copiada ou escrita independentemente.
Dawood ibn Kareem
191

Que tal usar o StringUtils.countMatches do Apache Commons Lang?

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

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

Isso gera:

3
SOU
fonte
9
Não importa o quão certo desta sugestão é, não pode ser aceite como a solução, uma vez que não está respondendo à pergunta de OP
kommradHomer
3
É este obsoleto ou algo .. o meu IDE não está reconhecendo
Vamsi Pavan Mahesh
@VamsiPavanMahesh StringUtils é uma biblioteca do Apache Commons. Verifique aqui: commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/…
Anup
Esta resposta é uma cópia da resposta de Peter Lawrey um dia antes (veja abaixo).
Zon
StringUtilsnão tem countMatchesmétodo.
Plaidshirt #
117

Seu lastIndex += findStr.length();foi colocado fora dos colchetes, causando um loop infinito (quando nenhuma ocorrência foi encontrada, lastIndex sempre foifindStr.length() ).

Aqui está a versão fixa:

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);
Olivier
fonte
92

Uma versão mais curta. ;)

String str = "helloslkhellodjladfjhello";
String findStr = "hello";
System.out.println(str.split(findStr, -1).length-1);
Peter Lawrey
fonte
8
return haystack.split(Pattern.quote(needle), -1).length - 1;se por exemploneedle=":)"
Mr_and_Mrs_D
2
@lOranger Sem o, ,-1ele soltará as partidas finais.
31812 Peter Lawrey
3
Ai, obrigado, bom saber! Isso vai me ensinar a ler as pequenas linhas no javadoc ...
Laurent Grégoire
4
Agradável! Mas inclui apenas correspondências não sobrepostas, não? Por exemplo, a correspondência de "aa" em "aaa" retornará 1, não 2? É claro que a inclusão de correspondências sobrepostas ou não sobrepostas é válida e depende dos requisitos do usuário (talvez um sinalizador para indicar sobreposições de contagem, sim / não)?
quer
2
-1 .. tente executar isso em "aaaa" e "aa" .. a resposta correta é 3 e não 2. #
Kalyanaraman Santhanam
79

Você realmente tem que lidar com a correspondência? Especialmente se tudo o que você precisa é o número de ocorrências, as expressões regulares são mais organizadas:

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);     
Jean
fonte
1
Isso NÃO encontra caracteres especiais, ele encontrará 0 para as seqüências de caracteres abaixo: String str = "hel+loslkhel+lodjladfjhel+lo"; Pattern p = Pattern.compile("hel+lo");
Ben
13
sim, se você expressar seu regex corretamente. tente com Pattern.compile("hel\\+lo");o +sinal tem um significado especial em uma regex e precisa ser escapado.
Jean
4
Se o que você está procurando é pegar uma String arbitrária e usá-la como uma correspondência exata com todos os caracteres especiais de expressão regular ignorados, Pattern.quote(str)é seu amigo!
Mike Furtak
2
isso não funciona para "aaa" quando str = "aaaaaa". Existem 4 respostas, mas a sua está dando 2
Pujan Srivastava
Esta solução não funciona para este caso: str = "Este é um teste \\ n \\ r string", subStr = "\\ r", mostra 0 ocorrências.
Maksym Ovsianikov 01/12/19
19

Estou muito surpreso que ninguém tenha mencionado esse forro. É simples, conciso e tem um desempenho um pouco melhor do questr.split(target, -1).length-1

public static int count(String str, String target) {
    return (str.length() - str.replace(target, "").length()) / target.length();
}
kmecpp
fonte
Deve ser a melhor resposta. Obrigado!
Lakam99 21/11/19
12

Aqui está, embrulhado em um método agradável e reutilizável:

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;
}
mmm
fonte
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);

no final da contagem de loop é 3; espero que ajude

dfa
fonte
5
O código contém um erro. Se procurarmos por um único caractere, o findStr.length() - 1retorno é 0 e estamos em um ciclo sem fim.
Jan Bodnar
6

Muitas das respostas fornecidas falham em um ou mais dos seguintes:

  • Padrões de comprimento arbitrário
  • Correspondências sobrepostas (como contar "232" em "23232" ou "aa" em "aaa")
  • Meta-caracteres de expressão regular

Aqui está o que eu escrevi:

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;
}

Chamada de exemplo:

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

Se você deseja uma pesquisa sem expressão regular, compile seu padrão adequadamente com o LITERALsinalizador:

Pattern pattern = Pattern.compile("1+1", Pattern.LITERAL);
int count = countMatches(pattern, "1+1+1"); // Returns 2
benkc
fonte
Sim ... surpreso que não haja algo assim no Apache StringUtils.
mike roedor
6
public int countOfOccurrences(String str, String subStr) {
  return (str.length() - str.replaceAll(Pattern.quote(subStr), "").length()) / subStr.length();
}
Maksym Ovsianikov
fonte
Boa resposta. Você pode se importar em adicionar algumas notas sobre como isso funciona?
santhosh Kumar
Claro, str - é a nossa fonte, subStr - é uma substring. O objetivo é calcular a quantidade de ocorrências de subStr em str. Para fazer isso, usamos a fórmula: (ab) / c, onde a - comprimento de str, b - comprimento de str sem todas as ocorrências de subStr (removemos todas as ocorrências de subStr de str para isso), c - comprimento de subStr . Então, basicamente extraímos do comprimento de str - length de str sem todo subStr, e depois dividimos o resultado no comprimento de subStr. Entre em contato se tiver outras dúvidas.
Maksym Ovsianikov
Santhosh, de nada! A parte importante é usar Pattern.quote para subStr, caso contrário, pode falhar em alguns casos, como este: str = "Este é um teste \\ n \\ r string", subStr = "\\ r". Algumas respostas semelhantes fornecidas aqui não usam Pattern; portanto, elas falharão nesses casos.
Maksym Ovsianikov 01/12/2017
Não há razão para regex, use replace, not replaceAll.
NateS
3

Incremente lastIndexsempre que procurar a próxima ocorrência.

Caso contrário, ele sempre encontrará a primeira substring (na posição 0).

Stanislav Kniazev
fonte
3
public int indexOf(int ch,
                   int fromIndex)

Retorna o índice dentro dessa cadeia de caracteres da primeira ocorrência do caractere especificado, iniciando a pesquisa no índice especificado.

Portanto, seu lastindexvalor é sempre 0 e sempre encontra olá na string.

Bhushan Bhangale
fonte
2

A resposta dada como correta não é boa para contar coisas como retornos de linha e é muito detalhada. Respostas posteriores são melhores, mas tudo pode ser alcançado simplesmente com

str.split(findStr).length

Não descarta correspondências finais usando o exemplo na pergunta.

Marca
fonte
1
Isso já foi coberto em outra resposta ; e essa resposta também melhorou.
michaelb958 - GoFundMonica
1
Este deve ser um comentário sobre a resposta em questão, não outra resposta.
James.garriss
2

Você pode numerar ocorrências usando a função de biblioteca embutida:

import org.springframework.util.StringUtils;
StringUtils.countOccurrencesOf(result, "R-")
Vencedor
fonte
1
Não funciona, você deve especificar a dependência que usou.
Saikat 30/05
1

tente adicionar lastIndex+=findStr.length()ao final do seu loop, caso contrário, você terminará em um loop sem fim, porque depois de encontrar a substring, você está tentando encontrá-lo repetidamente da mesma última posição.

Thorsten Schleinzer
fonte
1

Tente este. Ele substitui todos os jogos por um -.

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

E se você não quer destruir o seu, strvocê pode criar uma nova string com o mesmo conteúdo:

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

Depois de executar este bloco, estes serão seus valores:

str = "helloslkhellodjladfjhello"
strDestroy = "-slk-djladfj-"
findStr = "hello"
numberOfMatches = 3
Xander
fonte
1

Como @Mr_and_Mrs_D sugeriu:

String haystack = "hellolovelyworld";
String needle = "lo";
return haystack.split(Pattern.quote(needle), -1).length - 1;
Ron Tesler
fonte
1

Com base nas respostas existentes, gostaria de adicionar uma versão "mais curta" sem o 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
fonte
este leva em consideração se a sequência se repete, por exemplo, se você estiver procurando a sequência 'xx' em uma sequência 'xxx'.
tCoe 29/09/16
1

Aqui está a versão avançada para contar quantas vezes o token ocorreu em uma sequência inserida pelo usuário:

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
fonte
1

Este método abaixo mostra quantas vezes a substring se repete em toda a sua string. Espero usar completo para você: -

    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++;
        }

    }
duggu
fonte
0

aqui está a outra solução sem o uso de regexp / patterns / matchers ou mesmo sem o uso de 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);
Arun Kumar Mudraboyina
fonte
0

Se você precisar do índice de cada substring na string original, poderá fazer algo com o indexOf assim:

 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;
}
Rinoceronte
fonte
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;

}

Nikolai Nechai
fonte
esta questão é de 8 anos, e sem qualquer indicação de por que isso é uma solução melhor do que os outros 22 soluções postou, ele provavelmente deve ser removido
Jason Wheeler
0

Esta solução imprime o número total de ocorrências de uma determinada substring em toda a cadeia, incluindo também os casos em que existem correspondências sobrepostas.

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);
    }
}
Anubhav Singh
fonte