Java: compruebe si la cadena comienza con otra cadena

En este tutorial, repasaremos ejemplos de cómo verificar si una cadena comienza con una subcadena en Java. Cubriremos los métodos Core Java y Apache Commons.

Introducción

En este artículo, veremos cómo verificar si una cadena comienza con otra cadena en Java.

Esta es una tarea bastante común en programación, y es muy similar a comprobar si una cadena contiene una subcadena. Por ejemplo, esto puede ser útil si queremos filtrar todas las palabras de una gran selección que comienza con una cadena específica.

Sin embargo, este problema difiere de verificar si una cadena solo contiene una subcadena específica, y hay algunas formas de hacerlo tanto en Core Java como en Apache Commons:

Core Java

Comencemos con soluciones que se pueden implementar fácilmente usando Core Java.

Cadena.comienzaCon()

Este método hace exactamente lo que necesitamos que haga, y es la forma más común de resolver este problema. Devuelve un booleano, que indica si la cadena comienza con la palabra clave proporcionada:

1
2
3
4
5
String string = "every end is a new beginning";

System.out.println(string.toLowerCase().startsWith("new".toLowerCase()));
System.out.println(string.toLowerCase().startsWith("EVERY".toLowerCase()));
System.out.println(string.toLowerCase().startsWith(""));

Ejecutando estas salidas:

1
2
3
false
true
true

Nota: Si el parámetro pasado es una cadena vacía, el resultado siempre es verdadero.

Por supuesto, este método distingue entre mayúsculas y minúsculas, y siempre debe usarse junto con toLowerCase() o toUpperCase() cuando solo estamos buscando una palabra clave específica y no nos importa si los casos coinciden.

Stream.anyMatch()

Otra cosa que podríamos comprobar es si una cadena comienza con varias subcadenas. Digamos que tenemos algunos prefijos estandarizados: podemos usar hacer un Stream de subcadenas y ejecutar una verificación anyMatch() para la cadena que estamos registrando.

Echemos un vistazo a cómo hacerlo:

1
2
String string = "every end is a new beginning";
System.out.println(Stream.of("every", "none").anyMatch(string::startsWith));

Aquí, hemos creado un ‘Stream’ de posibles subcadenas, y verificamos si alguna de ellas está presente al comienzo de la Cadena dada con una llamada de referencia de método a ‘startsWith()’.

Este código da como resultado:

1
true

String.indexOf()

El método indexOf() puede ser bastante útil para resolver una variedad de problemas relacionados con las subcadenas, incluida la verificación de si una cadena comienza con una específica.

El método devuelve el índice de la primera aparición de una subcadena dentro de una cadena si se encuentra, de lo contrario -1.

Tiene algunas variantes sobrecargadas de las cuales solo necesitaremos la de abajo, ya que las otras tienen aparatos diferentes:

1
public int indexOf(String str)

Si el método indexOf() devuelve 0, eso significa que nuestra cadena comienza con otra que le hemos dado como parámetro.

Por ejemplo:

1
2
3
4
5
String string = "Just a sample String";

System.out.println(string.toLowerCase().indexOf("just".toLowerCase()));
System.out.println(string.toLowerCase().indexOf("String".toLowerCase()));
System.out.println(string.toLowerCase().indexOf("something else".toLowerCase()));

dará salida:

1
2
3
0
14
-1
  • La respuesta que estamos buscando se encuentra en nuestro primer ejemplo, ya que devolvió 0 - exactamente lo que necesitamos, lo que significa que nuestra cadena comienza con un parámetro dado.
  • Podemos ver claramente que en el segundo ejemplo, el parámetro "String" que hemos dado se encuentra dentro de nuestro String, pero en la posición 14, que no es lo que estábamos buscando.
  • El tercer ejemplo ni siquiera está contenido en nuestra Cadena y devuelve -1.

Usando la información anterior, podemos acumular la lógica en una función:

1
2
3
public static boolean startsWithSubstring(String text, String keyword) {
    return text.toLowerCase().indexOf(keyword.toLowerCase()) == 0;
}

Patrón con Regex y Matcher

La clase Pattern es una representación compilada de una expresión regular. Con este Patrón podemos generar un motor que reconozca dicha expresión regular - podemos generar un Matcher.

Usaremos el método find() en combinación con start() para verificar si nuestra instancia de Matcher comienza con una Cadena dada:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public static boolean startsWithSubstring(String text, String keyword) {
    String inputString = text.toLowerCase();
    String subString = keyword.toLowerCase();


    // We compile the regular expression to generate a Pattern object
    Pattern pattern = Pattern.compile(subString);

    // Then we generate an engine (Matcher) which can be used
    // to recognize and match the regular expression it was
    // generated from (in our case "this").
    Matcher matcher = pattern.matcher(inputString);

    // find() compares the assigned and compiled patterns, and will return a boolean value indicating if they match.
    // That's where the start() method comes into play; it returns the index of the position 
    // where the two strings matched, or -1 if it's not found.
    if (matcher.find()) {
          return matcher.start() == 0;
    }
    return false;
}

Probemos este método:

1
System.out.println(startsWithSubstring(string, "every"));

Esto resulta en:

1
true

Usando un for-loop

Una forma más de bajo nivel de resolver este problema sería usar un bucle for.

Recorremos toda la longitud de la cadena de búsqueda, comparamos los primeros caracteres searchString.length() y devolvemos true si todos coinciden.

Veamos cómo funciona todo esto en código:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public static boolean startsWithSubstring(String text, String keyword) {
    for (int i = 0; i < keyword.length(); i++) {
          if (text.toLowerCase().charAt(i) != keyword.toLowerCase().charAt(i)) {
              System.out.println("String doesn't start with " + "\"" + keyword + "\"");
        return false;
          } else if (i == keyword.length() - 1) {
              System.out.println("String starts with " + "\"" + keyword + "\"");
              return true;
          }
    }
    return false;
}

Probemos este método:

1
2
3
4
5
String string = "Just a sample String";

System.out.println(startsWithSubstring(string, "just"));
System.out.println(startsWithSubstring(string, "String"));
System.out.println(startsWithSubstring(string, "something else"));

Esto resulta en:

1
2
3
4
5
6
String starts with "just"
true
String doesn't start with "String"
false
String doesn't start with "something else"
false

De forma predeterminada, si no hubiéramos usado toLowerCase() para igualar las letras en el método en sí, este enfoque habría sido sensible a mayúsculas y minúsculas.

Apache Commons

La biblioteca Apache Commons proporciona una funcionalidad que se expande en el núcleo de Java Framework. Es una de las principales bibliotecas de terceros y está presente en muchos proyectos modernos de Java.

Apache Commons ofrece la clase StringUtils, que contiene muchos métodos utilizados para manipular cadenas. La mayoría de estos métodos son bastante similares a los que se encuentran en java.lang.String. La principal diferencia es que todos los métodos dentro de la clase StringUtils son seguros contra nulos.

Sin embargo, para esta tarea solo necesitaremos algunos métodos de esta clase:

  1. .indexOf()
  2. .empiezaCon()
  3. .startsWithIgnoreCase().

Si aún no lo ha hecho, incluya la biblioteca Apache Commons en su proyecto agregando una dependencia a su archivo pom.xml si está usando Maven:

1
2
3
4
5
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.11</version>
</dependency>

O agregándolo a Gradle:

1
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.11'

StringUtils.indexOf()

Este método tiene algunas variantes sobrecargadas, todas las cuales devuelven un valor int que indica el índice de la primera aparición de la subcadena, o -1 si la subcadena no aparece en absoluto.

Nos centraremos en la siguiente variante de este método:

1
public static int indexOf(CharSequence seq, CharSequence searchSeq)

Este método toma dos Strings/CharSequences.

El parámetro seq representa la cadena en la que buscaremos para encontrar searchSeq. Se aplica la misma lógica que antes: si esta función devuelve 0, sabremos que nuestra cadena comienza con una subcadena dada searchSeq.

También tenga en cuenta que este método, al igual que su contraparte, también distingue entre mayúsculas y minúsculas, por lo que se requiere usar .toLowerCase() o .toUpperCase() para lograr un comportamiento que no distinga entre mayúsculas y minúsculas.

Usar este método es muy similar a usar indexOf() de java.lang.String:

1
2
3
4
5
String string = "a simple string to search in";

System.out.println(StringUtils.indexOf(string.toLowerCase(), "a simple".toLowerCase()));
System.out.println(StringUtils.indexOf(string.toLowerCase(), "string".toLowerCase()));
System.out.println(StringUtils.indexOf(string.toLowerCase(), "something".toLowerCase()));

Esto producirá:

1
2
3
0
9
-1

Y por lo tanto, podemos usar esto como un método de conveniencia:

1
2
3
public static boolean startsWithSubstring(String text, String keyword) {
    return StringUtils.indexOf(text.toLowerCase(), keyword.toLowerCase()) == 0;
}

StringUtils.startsWith()

Este método hace exactamente lo que esperas. También distingue entre mayúsculas y minúsculas como sus predecesores y acepta los mismos dos parámetros.

Devuelve verdadero si el texto comienza con la palabra clave, o falso si no es así.

La comparación de dos valores ’nulos’ dará como resultado ‘verdadero’:

1
2
3
4
5
String string = "a simple string to search in";

System.out.println(StringUtils.startsWith(string.toLowerCase(), "A SIMPLE"));
System.out.println(StringUtils.startsWith(string.toLowerCase(), "A SIMPLE".toLowerCase()));
System.out.println(StringUtils.startsWith(null, null));

Ejecutar esto generará:

1
2
3
false
true
true

StringUtils.startsWithAny()

startsWithAny() no tiene una contraparte en la clase java.lang.String, y es exclusivo de StringUtils.

Sin embargo, es muy parecido a lo que hicimos con anyMatch() en un Stream: comprueba si una String comienza con alguna de las subcadenas dadas y devuelve true o false según corresponda.

También distingue entre mayúsculas y minúsculas:

1
2
3
4
5
6
String string = "a simple string to search in";

System.out.println(StringUtils.startsWithAny(string, "something", "a simple"));
System.out.println(StringUtils.startsWithAny(string, "something", "string"));
System.out.println(StringUtils.startsWithAny(string, "something", null));
System.out.println(StringUtils.startsWithAny(string, "something", ""));

Ejecutar esto nos da:

1
2
3
4
true
false
false
true

StringUtils.startsWithIgnoreCase()

Dado que puede resultar molesto llamar a toLowerCase() todo el tiempo en Strings durante la comparación, también puede demandar al método startsWithIgnoreCase().

Al igual que el método startsWith() de la misma clase, toma dos tipos de cadenas (o CharSequence), y el valor de retorno es verdadero o falso dependiendo de si el texto realmente comienza con la palabra clave proporcionada:

1
2
3
4
5
6
String string = "a simple string to search in";

System.out.println(StringUtils.startsWithIgnoreCase(string, "something"));
System.out.println(StringUtils.startsWithIgnoreCase(string, "A SIMPLE"));
System.out.println(StringUtils.startsWithIgnoreCase(string, ""));
System.out.println(StringUtils.startsWithIgnoreCase(string, null));

Ejecutando estas salidas:

1
2
3
4
false
true
true
false

Conclusión

En este artículo, hemos repasado todos los diferentes métodos para verificar si una ‘Cadena’ comienza con otra ‘Cadena’, tanto en el núcleo de Java como utilizando la biblioteca Apache Commons.

En conclusión, hay muchas maneras diferentes de llevar a cabo esta tarea. En realidad, podríamos haber combinado cualquiera de los métodos anteriores para encontrar un índice en el que comience una subcadena y, a partir de ahí, verificar dónde se ubica la subcadena.

La mayoría de las veces, simplemente usar los métodos startsWith() o startsWithIgnoreCase() sería suficiente para resolver este problema y también proporcionaría el código más limpio y breve.