Ocurrencias de subcadenas en una cadena

122

¿Por qué el siguiente algoritmo no se detiene para mí? (str es la cadena que estoy buscando, findStr es la cadena que estoy tratando de 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
fuente
8
Hicimos una muy buena en Udacity: usamos newSTR = str.replace (findStr, ""); y regresó count = ((str.length () - newSTR.length ()) / findStr.length ());
SolarLunix
Pregunta similar para los personajes: stackoverflow.com/q/275944/873282
koppor
¿No desea también tener en cuenta el caso donde el prefijo de la cadena de búsqueda es su sufijo? En ese caso, no creo que ninguna de las respuestas sugeridas funcione. Aquí hay un ejemplo. En ese caso, necesitaría un algoritmo más elaborado, como el Knuth Morris Pratt (KMP) que está codificado en el libro CLRS
Sid
no se detiene, porque después de alcanzar su condición de 'alto' (lastIndex == -1) lo restablece incrementando el valor de lastIndex (lastIndex + = findStr.length ();)
Legna

Respuestas:

83

La última línea estaba creando un problema. lastIndexnunca estaría en -1, por lo que habría un bucle infinito. Esto se puede solucionar moviendo la última línea de código al bloque 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
fuente
121
Esta respuesta es la copia exacta de la publicación que hice una hora antes;)
Olivier
8
Tenga en cuenta que esto podría o no devolver el resultado esperado. Con la subcadena "aa" y la cadena para buscar "aaa", el número de ocurrencias esperadas puede ser uno (devuelto por este código), pero también puede ser dos (en este caso necesitará "lastIndex ++" en lugar de "lastIndex + = findStr.length () ") dependiendo de lo que esté buscando.
Stanislav Kniazev el
@olivier no vio eso ... :( @stan eso es absolutamente correcto ... solo estaba arreglando el código en el problema ... supongo que depende de lo que signifique bobcom por número de ocurrencias en la cadena ...
codebreach
1
¿Cuándo van a aprender las personas a envolver cosas como esta en un método estático de copiar y pegar? Vea mi respuesta a continuación, también está más optimizado.
mmm
1
La moraleja aquí es que si tiene la intención de escribir una respuesta, verifique primero si alguien más ya ha escrito exactamente la misma respuesta. Realmente no hay ningún beneficio en que la misma respuesta aparezca dos veces, independientemente de si su respuesta fue copiada o escrita independientemente.
Dawood ibn Kareem
192

¿Qué tal usar StringUtils.countMatches de Apache Commons Lang?

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

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

Eso da salida:

3
A.M
fuente
9
No importa cuán correcta sea esta sugerencia, no puede aceptarse como la solución, ya que no responde a la pregunta de OP
kommradHomer
3
¿Está en desuso o algo así? Mi IDE no lo reconoce
Vamsi Pavan Mahesh
@VamsiPavanMahesh StringUtils es una biblioteca de Apache Commons. Marque aquí: commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/…
Anup
Esta respuesta es una copia de la respuesta de Peter Lawrey un día antes (ver abajo).
Zon
StringUtilsno tiene countMatchesmétodo
Plaidshirt
117

Su lastIndex += findStr.length();se colocó fuera de los corchetes, causando un bucle infinito (cuando no se encontró ninguna ocurrencia, lastIndex siempre fue así findStr.length()).

Aquí está la versión fija:

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
fuente
92

Una versión más corta ;)

String str = "helloslkhellodjladfjhello";
String findStr = "hello";
System.out.println(str.split(findStr, -1).length-1);
Peter Lawrey
fuente
8
return haystack.split(Pattern.quote(needle), -1).length - 1;si, por ejemploneedle=":)"
Mr_and_Mrs_D
2
@lOranger Sin el ,-1, dejará caer las coincidencias finales.
Peter Lawrey
3
¡Ay, gracias, es bueno saberlo! Esto me enseñará a leer las pequeñas líneas en el javadoc ...
Laurent Grégoire
44
¡Agradable! Pero solo incluye coincidencias no superpuestas, ¿no? Por ejemplo, hacer coincidir "aa" en "aaa" devolverá 1, no 2? Por supuesto, las coincidencias superpuestas o no superpuestas son válidas y dependen de los requisitos del usuario (¿quizás una bandera para indicar superposiciones de conteo, sí / no)?
Cornel Masson el
2
-1 .. intenta ejecutar esto en "aaaa" y "aa" .. la respuesta correcta es 3 no 2.
Kalyanaraman Santhanam
79

¿Realmente tienes que manejar el emparejamiento tú mismo? Especialmente si todo lo que necesita es el número de ocurrencias, las expresiones regulares son más ordenadas:

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);     
Vaquero
fuente
1
Esto NO encuentra caracteres especiales, encontrará 0 recuento para las cadenas a continuación: String str = "hel+loslkhel+lodjladfjhel+lo"; Pattern p = Pattern.compile("hel+lo");
Ben
13
Sí, lo hará si expresa su expresión regular correctamente. probar con Pattern.compile("hel\\+lo");el +signo tiene un significado especial en una expresión regular y necesita ser escapado.
Jean
44
Si lo que está buscando es tomar una cadena arbitraria y usarla como una coincidencia exacta con todos los caracteres especiales de expresión regular ignorados, ¡ Pattern.quote(str)es su amigo!
Mike Furtak
2
esto no funciona para "aaa" cuando str = "aaaaaa". Hay 4 respuestas, pero las suyas dan 2
Pujan Srivastava
Esta solución no funciona para este caso: str = "Esta es una cadena de prueba \\ n \\ r", subStr = "\\ r", muestra 0 ocurrencias.
Maksym Ovsianikov
19

Estoy muy sorprendido de que nadie haya mencionado este revestimiento. Es simple, conciso y funciona ligeramente mejor questr.split(target, -1).length-1

public static int count(String str, String target) {
    return (str.length() - str.replace(target, "").length()) / target.length();
}
kmecpp
fuente
Debería ser la mejor respuesta. ¡Gracias!
lakam99
12

Aquí está, envuelto en un método agradable y reutilizable:

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

al final del ciclo el conteo es 3; Espero eso ayude

dfa
fuente
55
El código contiene un error. Si buscamos un solo carácter, el findStr.length() - 1resultado es 0 y estamos en un ciclo sin fin.
Jan Bodnar
6

Muchas de las respuestas dadas fallan en uno o más de:

  • Patrones de longitud arbitraria.
  • Partidas superpuestas (como contar "232" en "23232" o "aa" en "aaa")
  • Metacaracteres de expresiones regulares

Esto es lo que escribí:

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

Llamada de ejemplo:

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

Si desea una búsqueda de expresión no regular, solo compile su patrón apropiadamente con la LITERALbandera:

Pattern pattern = Pattern.compile("1+1", Pattern.LITERAL);
int count = countMatches(pattern, "1+1+1"); // Returns 2
benkc
fuente
Sí ... sorprendido de que no haya algo como esto en 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
fuente
Buena respuesta. ¿Te importaría agregar algunas notas sobre cómo funciona?
santhosh kumar
Claro, str - es nuestra cadena fuente, subStr - es una subcadena. El objetivo es calcular la cantidad de ocurrencias de subStr en str. Para hacer esto, usamos la fórmula: (ab) / c, donde a - longitud de str, b - longitud de str sin todas las apariciones de subStr (eliminamos todas las apariciones de subStr de str para esto), c - longitud de subStr . Entonces, básicamente extraemos de la longitud de str - longitud de str sin todo subStr, y luego dividimos el resultado en la longitud de subStr. Avíseme si tiene alguna otra pregunta.
Maksym Ovsianikov
Santhosh, de nada! La parte importante es usar Pattern.quote para subStr, de lo contrario puede fallar en algunos casos, como este: str = "Esta es una prueba \\ n \\ r string", subStr = "\\ r". Algunas respuestas similares proporcionadas aquí no usan Patrón, por lo que fallarán en tales casos.
Maksym Ovsianikov
No hay razón para regex, use replace, no replaceAll.
Nates
3

Incremente lastIndexsiempre que busque la próxima ocurrencia.

De lo contrario, siempre se encuentra la primera subcadena (en la posición 0).

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

Devuelve el índice dentro de esta cadena de la primera aparición del carácter especificado, comenzando la búsqueda en el índice especificado.

Por lo tanto, su lastindexvalor siempre es 0 y siempre encuentra hola en la cadena.

Bhushan Bhangale
fuente
2

La respuesta dada como correcta no es buena para contar cosas como los retornos de línea y es demasiado detallada. Las respuestas posteriores son mejores, pero todo se puede lograr simplemente con

str.split(findStr).length

No deja caer las coincidencias finales utilizando el ejemplo de la pregunta.

marca
fuente
1
Esto ya se ha cubierto en otra respuesta ; y esa respuesta también lo hizo mejor.
michaelb958 - GoFundMonica
1
Esto debería ser un comentario sobre la respuesta en cuestión, no otra respuesta.
james.garriss
2

Puede hacer un número de ocurrencias usando la función de biblioteca incorporada:

import org.springframework.util.StringUtils;
StringUtils.countOccurrencesOf(result, "R-")
Víctor
fuente
1
No funciona, debe especificar la dependencia que utilizó.
Saikat
1

intente agregar lastIndex+=findStr.length()al final de su ciclo, de lo contrario terminará en un ciclo sin fin porque una vez que encontró la subcadena, está tratando de encontrarla una y otra vez desde la misma última posición.

Thorsten Schleinzer
fuente
1

Prueba este. Reemplaza todos los partidos con a -.

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

Y si no quieres destruir tu strpuedes crear una nueva cadena con el mismo contenido:

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

Después de ejecutar este bloque, estos serán sus valores:

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

Como @Mr_and_Mrs_D sugirió:

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

Según las respuestas existentes, me gustaría agregar una versión "más corta" sin el 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
fuente
este tiene en cuenta si la cadena se repite, por ejemplo, si está buscando la cadena 'xx' en una cadena 'xxx'.
tCoe
1

Aquí está la versión avanzada para contar cuántas veces se produjo el token en una cadena ingresada por el usuario:

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

El siguiente método muestra cuántas veces se repite la subcadena en su cadena completa. Espero uso completo para usted: -

    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
fuente
0

Aquí está la otra solución sin usar regexp / patterns / matchers o incluso sin usar 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
fuente
0

Si necesita el índice de cada subcadena dentro de la cadena original, puede hacer algo con indexOf de esta manera:

 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
fuente
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
fuente
esta pregunta tiene 8 años, y sin ninguna indicación de por qué esta es una mejor solución que las otras 22 soluciones publicadas, probablemente debería eliminarse
Jason Wheeler
0

Esta solución imprime el número total de ocurrencias de una subcadena dada en toda la cadena, también incluye los casos en los que existen coincidencias superpuestas.

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
fuente