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

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

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

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

92

Una versión más corta ;)

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

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

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

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


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

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

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


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.


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.


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

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.


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

1

Como @Mr_and_Mrs_D sugirió:

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

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

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

}

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

    }

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

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

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;

}


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);
    }
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.