Occorrenze di sottostringa in una stringa


122

Perché il seguente algoritmo non si ferma per me? (str è la stringa che sto cercando, findStr è la stringa che sto cercando di trovare)

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
Ne abbiamo fatto uno veramente buono in Udacity: abbiamo usato newSTR = str.replace (findStr, ""); e restituito count = ((str.length () - newSTR.length ()) / findStr.length ());
SolarLunix

Domanda simile per i caratteri: stackoverflow.com/q/275944/873282
koppor

Non vuoi tenere conto anche del caso in cui il prefisso della stringa di ricerca è il suo suffisso? In tal caso non penso che nessuna delle risposte suggerite funzionerebbe. ecco un esempio. In tal caso avresti bisogno di un algoritmo più elaborato, come Knuth Morris Pratt (KMP) che è codificato nel libro CLRS
Sid

non si ferma per te, perché dopo aver raggiunto la tua condizione di 'arresto' (lastIndex == -1) lo resetti incrementando il valore di lastIndex (lastIndex + = findStr.length ();)
Legna

Risposte:


83

L'ultima riga stava creando un problema. lastIndexnon sarebbe mai a -1, quindi ci sarebbe un ciclo infinito. Questo può essere risolto spostando l'ultima riga di codice nel blocco 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);

121
Questa risposta è la copia esatta del post che ho fatto un'ora prima;)
Olivier

8
Notare che questo potrebbe o meno restituire il risultato atteso. Con la sottostringa "aa" e la stringa da cercare "aaa" il numero di occorrenze attese potrebbe essere uno (restituito da questo codice), ma anche due (in questo caso avrai bisogno di "lastIndex ++" invece di "lastIndex + = findStr.length () ") a seconda di ciò che stai cercando.
Stanislav Kniazev,

@olivier non l'ha visto ... :( @stan è assolutamente corretto ... stavo solo aggiustando il codice nel problema ... immagino che dipenda da cosa significa bobcom per numero di occorrenze nella stringa ...
codebreach

1
Quando le persone impareranno a racchiudere cose come questa in un metodo statico di copia e incolla? Vedi la mia risposta di seguito, è anche più ottimizzata.
mmm

1
La morale qui è che se hai intenzione di scrivere una risposta, controlla prima se qualcun altro ha già scritto la stessa identica risposta. Non c'è davvero alcun vantaggio nell'avere la stessa risposta apparsa due volte, indipendentemente dal fatto che la tua risposta sia stata copiata o scritta indipendentemente.
Dawood ibn Kareem

192

Che ne dici di usare StringUtils.countMatches da Apache Commons Lang?

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

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

Che produce:

3

9
Non importa quanto sia corretto questo suggerimento, non può essere accettato come soluzione in quanto non risponde alla domanda di OP
kommradHomer

3
È deprecato o qualcosa del genere .. il mio IDE non riconosce
Vamsi Pavan Mahesh

@VamsiPavanMahesh StringUtils è una libreria di Apache Commons. Controlla qui: commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/…
Anup

Questa risposta è una copia della risposta di Peter Lawrey un giorno prima (vedi sotto).
Zon

StringUtilsnon ha countMatchesmetodo.
plaidshirt

117

Il tuo è lastIndex += findStr.length();stato posizionato al di fuori delle parentesi, causando un ciclo infinito (quando non è stata trovata alcuna occorrenza, lastIndex era sempre a findStr.length()).

Ecco la versione fissa:

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

Una versione più breve. ;)

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;se per esempioneedle=":)"
Mr_and_Mrs_D

2
@lOranger Senza il ,-1cadrà le partite finali.
Peter Lawrey

3
Ahi, grazie, buono a sapersi! Questo mi insegnerà a leggere le piccole righe nel javadoc ...
Laurent Grégoire

4
Bello! Ma include solo corrispondenze non sovrapposte, no? Ad esempio, la corrispondenza "aa" in "aaa" restituirà 1, non 2? Ovviamente anche le corrispondenze sovrapposte o non sovrapposte sono valide e dipendono dai requisiti dell'utente (forse un flag per indicare le sovrapposizioni di conteggio, sì / no)?
Cornel Masson

2
-1 .. prova a eseguirlo su "aaaa" e "aa" .. la risposta corretta è 3 non 2.
Kalyanaraman Santhanam

79

Devi davvero gestire l'abbinamento da solo? Soprattutto se tutto ciò di cui hai bisogno è il numero di occorrenze, le espressioni regolari sono più ordinate:

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
Questo NON trova caratteri speciali, troverà il conteggio 0 per le stringhe seguenti: String str = "hel+loslkhel+lodjladfjhel+lo"; Pattern p = Pattern.compile("hel+lo");
Ben

13
sì, lo sarà se esprimi correttamente la tua regex. try con Pattern.compile("hel\\+lo");il +segno ha un significato speciale in una regex e deve essere sottoposto a escape.
Jean

4
Se quello che stai cercando è prendere una stringa arbitraria e usarla come una corrispondenza esatta con tutti i caratteri speciali delle espressioni regolari ignorati, Pattern.quote(str)è tuo amico!
Mike Furtak

2
questo non funziona per "aaa" quando str = "aaaaaa". Ci sono 4 risposte ma la tua dà 2
Pujan Srivastava

Questa soluzione non funziona in questo caso: str = "Questa è una stringa di prova \\ n \\ r", subStr = "\\ r", mostra 0 occorrenze.
Maksym Ovsianikov

19

Sono molto sorpreso che nessuno abbia menzionato questa linea. È semplice, conciso e funziona leggermente meglio distr.split(target, -1).length-1

public static int count(String str, String target) {
    return (str.length() - str.replace(target, "").length()) / target.length();
}

Dovrebbe essere la risposta migliore. Grazie!
lakam99

12

Eccolo, racchiuso in un metodo piacevole e riutilizzabile:

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

alla fine del ciclo il conteggio è 3; spero che sia d'aiuto


5
Il codice contiene un errore. Se cerchiamo un singolo carattere, findStr.length() - 1restituisce 0 e siamo in un ciclo infinito.
Jan Bodnar

6

Molte delle risposte fornite falliscono su uno o più di:

  • Modelli di lunghezza arbitraria
  • Corrispondenze sovrapposte (come contare "232" in "23232" o "aa" in "aaa")
  • Meta-caratteri delle espressioni regolari

Ecco cosa ho scritto:

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

Chiamata di esempio:

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

Se vuoi una ricerca con espressioni non regolari, compila il tuo pattern in modo appropriato con il LITERALflag:

Pattern pattern = Pattern.compile("1+1", Pattern.LITERAL);
int count = countMatches(pattern, "1+1+1"); // Returns 2

Sì ... sorpreso che non ci sia qualcosa di simile in Apache StringUtils.
mike rodent

6
public int countOfOccurrences(String str, String subStr) {
  return (str.length() - str.replaceAll(Pattern.quote(subStr), "").length()) / subStr.length();
}

Buona risposta. Ti dispiace aggiungere alcune note su come funziona?
santhosh kumar,

Certo, str - è la nostra stringa sorgente, subStr - è una sottostringa. L'obiettivo è calcolare la quantità di occorrenze di subStr in str. Per fare ciò, usiamo la formula: (ab) / c, dove a - lunghezza di str, b - lunghezza di str senza tutte le occorrenze di subStr (per questo rimuoviamo tutte le occorrenze di subStr da str), c - lunghezza di subStr . Quindi, fondamentalmente estraiamo dalla lunghezza di str - lunghezza di str senza tutti i subStr, e poi dividiamo il risultato sulla lunghezza di subStr. Per favore fatemi sapere se avete altre domande.
Maksym Ovsianikov

Santhosh, sei il benvenuto! La parte importante è usare Pattern.quote per subStr, altrimenti in alcuni casi potrebbe fallire, come questo: str = "Questa è una stringa di prova \\ n \\ r", subStr = "\\ r". Alcune risposte simili fornite qui non usano Pattern, quindi in questi casi falliranno.
Maksym Ovsianikov

Non c'è motivo per regex, usa replace, no replaceAll.
NateS

3

Aumenta lastIndexogni volta che cerchi l'occorrenza successiva.

Altrimenti trova sempre la prima sottostringa (nella posizione 0).


3
public int indexOf(int ch,
                   int fromIndex)

Restituisce l'indice all'interno di questa stringa della prima occorrenza del carattere specificato, iniziando la ricerca dall'indice specificato.

Quindi il tuo lastindexvalore è sempre 0 e trova sempre ciao nella stringa.


2

La risposta data come corretta non è valida per contare cose come i ritorni di riga ed è troppo prolissa. Le risposte successive sono migliori ma tutto può essere ottenuto semplicemente con

str.split(findStr).length

Non elimina le corrispondenze finali utilizzando l'esempio nella domanda.


1
Questo è stato già trattato in un'altra risposta ; e anche quella risposta ha funzionato meglio.
michaelb958 - GoFundMonica

1
Questo dovrebbe essere un commento alla risposta in questione, non un'altra risposta.
james.garriss

2

È possibile il numero di occorrenze utilizzando la funzione di libreria incorporata:

import org.springframework.util.StringUtils;
StringUtils.countOccurrencesOf(result, "R-")

1
Non funziona, devi specificare la dipendenza che hai usato.
Saikat

1

prova ad aggiungere lastIndex+=findStr.length()alla fine del tuo ciclo, altrimenti finirai in un ciclo infinito perché una volta trovata la sottostringa, stai cercando di trovarla ancora e ancora dalla stessa ultima posizione.


1

Prova questo. Sostituisce tutte le partite con una -.

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

E se non vuoi distruggere il strtuo puoi creare una nuova stringa con lo stesso contenuto:

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

Dopo aver eseguito questo blocco, questi saranno i tuoi valori:

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

1

Come suggerito da @Mr_and_Mrs_D:

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

1

In base alle risposte esistenti, vorrei aggiungere una versione "più breve" senza se:

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

questo tiene conto se la stringa si ripete, ad esempio se stai cercando la stringa "xx" in una stringa "xxx".
tCoe

1

Ecco la versione avanzata per contare quante volte il token si è verificato in una stringa inserita dall'utente:

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

}

1

Il seguente metodo mostra quante volte la sottostringa si ripete sull'intera stringa. Spero di usarti completamente: -

    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

ecco l'altra soluzione senza usare regexp / patterns / matchers o anche senza usare 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

Se hai bisogno dell'indice di ogni sottostringa all'interno della stringa originale, puoi fare qualcosa con indexOf in questo modo:

 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;

}


questa domanda ha 8 anni e senza alcuna indicazione del motivo per cui questa è una soluzione migliore rispetto alle altre 22 soluzioni pubblicate, probabilmente dovrebbe essere rimossa
Jason Wheeler

0

Questa soluzione stampa il numero totale di occorrenze di una data sottostringa in tutta la stringa, include anche i casi in cui esistono corrispondenze sovrapposte.

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);
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.