¿Cómo comprobar si una cadena contiene otra Cadena sin distinguir mayúsculas de minúsculas en Java?


Digamos que tengo dos cuerdas,

String s1 = "AbBaCca";
String s2 = "bac";

Quiero realizar una comprobación de regreso s2 está contenido dentro de s1. Puedo hacer esto con:

return s1.contains(s2);

Estoy bastante seguro de que contains() distingue entre mayúsculas y minúsculas, sin embargo, no puedo determinar esto con seguridad al leer la documentación. Si lo es, entonces supongo que mi mejor método sería algo como:

return s1.toLowerCase().contains(s2.toLowerCase());

Todo esto aparte, ¿hay otra (posiblemente mejor) manera de lograr esto sin preocuparse por la sensibilidad a las mayúsculas y minúsculas?

 313
Author: Tunaki, 2008-09-17

17 answers

Sí, contiene distingue entre mayúsculas y minúsculas. Puedes usar java.útil.regex.Patrón con la bandera CASE_INSENSITIVE para coincidencia insensible a mayúsculas y minúsculas:

Pattern.compile(Pattern.quote(s2), Pattern.CASE_INSENSITIVE).matcher(s1).find();

EDITAR: Si s2 contiene caracteres especiales regex (de los cuales hay muchos) es importante citarlo primero. He corregido mi respuesta ya que es la primera que la gente verá, pero vote a favor de Matt Quail desde que señaló esto.

 281
Author: Dave L.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-27 22:21:48

Un problema con la respuesta de Dave L. es cuando s2 contiene regex markup como \d, etc.

Desea llamar a Pattern.cita () en s2:

Pattern.compile(Pattern.quote(s2), Pattern.CASE_INSENSITIVE).matcher(s1).find();
 232
Author: Matt Quail,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:26

Puede usar

org.apache.commons.lang3.StringUtils.containsIgnoreCase("AbBaCca", "bac");

La biblioteca Apache Commons es muy útil para este tipo de cosas. Y este en particular puede ser mejor que las expresiones regulares como regex es siempre caro en términos de rendimiento.

 122
Author: muhamadto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-30 16:42:31

Una Implementación Más Rápida: Utilizando String.regionMatches()

Usar regexp puede ser relativamente lento. (Ser lento) no importa si solo desea verificar en un caso. Pero si tienes una matriz o una colección de miles o cientos de miles de cadenas, las cosas pueden ser bastante lentas.

La solución presentada a continuación no utiliza expresiones regulares ni toLowerCase() (que también es lenta porque crea otras cadenas y simplemente las tira después de la comprobación).

La solución se basa en el Cuerda.regionMatches() método que parece ser desconocido. Comprueba si 2 String regiones coinciden, pero lo importante es que también tiene una sobrecarga con un práctico parámetro ignoreCase.

public static boolean containsIgnoreCase(String src, String what) {
    final int length = what.length();
    if (length == 0)
        return true; // Empty string is contained

    final char firstLo = Character.toLowerCase(what.charAt(0));
    final char firstUp = Character.toUpperCase(what.charAt(0));

    for (int i = src.length() - length; i >= 0; i--) {
        // Quick check before calling the more expensive regionMatches() method:
        final char ch = src.charAt(i);
        if (ch != firstLo && ch != firstUp)
            continue;

        if (src.regionMatches(true, i, what, 0, length))
            return true;
    }

    return false;
}

Análisis de velocidad

Este análisis de velocidad no significa ser ciencia de cohetes, solo una imagen aproximada de lo rápido que son los diferentes métodos.

Comparo 5 métodos.

  1. Nuestro método containsIgnoreCase().
  2. Por convertir ambas cadenas a minúsculas y llamar String.contains().
  3. Convirtiendo la cadena de origen a minúscula y llamando a String.contains() con la subcadena precalentada en caja inferior. Esta solución ya no es tan flexible porque prueba una subcadena predefiend.
  4. Usando expresión regular (la respuesta aceptada Pattern.compile().matcher().find()...)
  5. Usando una expresión regular pero con Pattern pre-creado y almacenado en caché. Esta solución ya no es tan flexible porque prueba un subcadena.

Resultados (llamando al método 10 millones de veces):

  1. Nuestro método: 670 ms
  2. 2x toLowerCase () y contiene (): 2829 ms
  3. 1x toLowerCase() y contiene () con subcadena en caché: 2446 ms
  4. Regexp: 7180 ms
  5. Regexp con caché Pattern: 1845 ms

Resulta en una tabla:

                                            RELATIVE SPEED   1/RELATIVE SPEED
 METHOD                          EXEC TIME    TO SLOWEST      TO FASTEST (#1)
------------------------------------------------------------------------------
 1. Using regionMatches()          670 ms       10.7x            1.0x
 2. 2x lowercase+contains         2829 ms        2.5x            4.2x
 3. 1x lowercase+contains cache   2446 ms        2.9x            3.7x
 4. Regexp                        7180 ms        1.0x           10.7x
 5. Regexp+cached pattern         1845 ms        3.9x            2.8x

Nuestro método es 4 veces más rápido en comparación con las minúsculas y el uso contains(), 10 veces más rápido en comparación con el uso de expresiones regulares y también 3 veces más rápido incluso si el Pattern está pre-almacenado en caché (y la pérdida de flexibilidad de la comprobación de una subcadena arbitraria).


Código de Prueba de Análisis

Si está interesado en cómo se realizó el análisis, aquí está la aplicación ejecutable completa:

import java.util.regex.Pattern;

public class ContainsAnalysis {

    // Case 1 utilizing String.regionMatches()
    public static boolean containsIgnoreCase(String src, String what) {
        final int length = what.length();
        if (length == 0)
            return true; // Empty string is contained

        final char firstLo = Character.toLowerCase(what.charAt(0));
        final char firstUp = Character.toUpperCase(what.charAt(0));

        for (int i = src.length() - length; i >= 0; i--) {
            // Quick check before calling the more expensive regionMatches()
            // method:
            final char ch = src.charAt(i);
            if (ch != firstLo && ch != firstUp)
                continue;

            if (src.regionMatches(true, i, what, 0, length))
                return true;
        }

        return false;
    }

    // Case 2 with 2x toLowerCase() and contains()
    public static boolean containsConverting(String src, String what) {
        return src.toLowerCase().contains(what.toLowerCase());
    }

    // The cached substring for case 3
    private static final String S = "i am".toLowerCase();

    // Case 3 with pre-cached substring and 1x toLowerCase() and contains()
    public static boolean containsConverting(String src) {
        return src.toLowerCase().contains(S);
    }

    // Case 4 with regexp
    public static boolean containsIgnoreCaseRegexp(String src, String what) {
        return Pattern.compile(Pattern.quote(what), Pattern.CASE_INSENSITIVE)
                    .matcher(src).find();
    }

    // The cached pattern for case 5
    private static final Pattern P = Pattern.compile(
            Pattern.quote("i am"), Pattern.CASE_INSENSITIVE);

    // Case 5 with pre-cached Pattern
    public static boolean containsIgnoreCaseRegexp(String src) {
        return P.matcher(src).find();
    }

    // Main method: perfroms speed analysis on different contains methods
    // (case ignored)
    public static void main(String[] args) throws Exception {
        final String src = "Hi, I am Adam";
        final String what = "i am";

        long start, end;
        final int N = 10_000_000;

        start = System.nanoTime();
        for (int i = 0; i < N; i++)
            containsIgnoreCase(src, what);
        end = System.nanoTime();
        System.out.println("Case 1 took " + ((end - start) / 1000000) + "ms");

        start = System.nanoTime();
        for (int i = 0; i < N; i++)
            containsConverting(src, what);
        end = System.nanoTime();
        System.out.println("Case 2 took " + ((end - start) / 1000000) + "ms");

        start = System.nanoTime();
        for (int i = 0; i < N; i++)
            containsConverting(src);
        end = System.nanoTime();
        System.out.println("Case 3 took " + ((end - start) / 1000000) + "ms");

        start = System.nanoTime();
        for (int i = 0; i < N; i++)
            containsIgnoreCaseRegexp(src, what);
        end = System.nanoTime();
        System.out.println("Case 4 took " + ((end - start) / 1000000) + "ms");

        start = System.nanoTime();
        for (int i = 0; i < N; i++)
            containsIgnoreCaseRegexp(src);
        end = System.nanoTime();
        System.out.println("Case 5 took " + ((end - start) / 1000000) + "ms");
    }

}
 97
Author: icza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-26 15:08:59

Sí, esto es alcanzable:

String s1 = "abBaCca";
String s2 = "bac";

String s1Lower = s1;

//s1Lower is exact same string, now convert it to lowercase, I left the s1 intact for print purposes if needed

s1Lower = s1Lower.toLowerCase();

String trueStatement = "FALSE!";
if (s1Lower.contains(s2)) {

    //THIS statement will be TRUE
    trueStatement = "TRUE!"
}

return trueStatement;

Este código devolverá la cadena "TRUE!"ya que encontró que tus personajes estaban contenidos.

 17
Author: Bilbo Baggins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-03 21:10:21

Una forma más sencilla de hacer esto (sin preocuparse por la coincidencia de patrones) sería convertir ambas Strings a minúsculas:

String foobar = "fooBar";
String bar = "FOO";
if (foobar.toLowerCase().contains(bar.toLowerCase()) {
    System.out.println("It's a match!");
}
 15
Author: Phil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-16 17:29:04

Puedes usar expresiones regulares , y funciona:

boolean found = s1.matches("(?i).*" + s2+ ".*");
 6
Author: Shiv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-30 16:44:30

Aquí hay algunos compatibles con Unicode que puede hacer si extrae ICU4j. Supongo que "ignorar mayúsculas y minúsculas" es cuestionable para los nombres de los métodos porque aunque las comparaciones de fuerza primaria ignoran mayúsculas y minúsculas, se describe como que los detalles dependen de la configuración regional. Pero es de esperar que dependa de la configuración regional de una manera que el usuario esperaría.

public static boolean containsIgnoreCase(String haystack, String needle) {
    return indexOfIgnoreCase(haystack, needle) >= 0;
}

public static int indexOfIgnoreCase(String haystack, String needle) {
    StringSearch stringSearch = new StringSearch(needle, haystack);
    stringSearch.getCollator().setStrength(Collator.PRIMARY);
    return stringSearch.first();
}
 3
Author: Trejkaz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-27 14:29:18

Hice una prueba para encontrar una coincidencia insensible a mayúsculas y minúsculas de una cadena. Tengo un Vector de 150.000 objetos con una Cadena como un campo y quería encontrar el subconjunto que coincide con una cadena. Probé tres métodos:

  1. Convertir todo a minúsculas

    for (SongInformation song: songs) {
        if (song.artist.toLowerCase().indexOf(pattern.toLowercase() > -1) {
                ...
        }
    }
    
  2. Use el método String matches ()

    for (SongInformation song: songs) {
        if (song.artist.matches("(?i).*" + pattern + ".*")) {
        ...
        }
    }
    
  3. Usar expresiones regulares

    Pattern p = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
    Matcher m = p.matcher("");
    for (SongInformation song: songs) {
        m.reset(song.artist);
        if (m.find()) {
        ...
        }
    }
    

Los resultados del tiempo son:

  • Ningún intento de coincidencia: 20 msec

  • To coincidencia inferior: 182 msec

  • Coincidencias de cadena: 278 msec

  • Expresión regular: 65 msec

La expresión regular parece ser la más rápida para este caso de uso.

 2
Author: Jan Newmarch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-30 16:47:47

No estoy seguro de cuál es su pregunta principal aquí, pero sí, .contiene distingue entre mayúsculas y minúsculas.

 1
Author: SCdF,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-17 19:42:16
String container = " Case SeNsitive ";
String sub = "sen";
if (rcontains(container, sub)) {
    System.out.println("no case");
}

public static Boolean rcontains(String container, String sub) {

    Boolean b = false;
    for (int a = 0; a < container.length() - sub.length() + 1; a++) {
        //System.out.println(sub + " to " + container.substring(a, a+sub.length()));
        if (sub.equalsIgnoreCase(container.substring(a, a + sub.length()))) {
            b = true;
        }
    }
    return b;
}

, Básicamente, es un método que toma dos cadenas. Se supone que es una versión de contains () que no distingue entre mayúsculas y minúsculas. Al usar el método contains, desea ver si una cadena está contenida en la otra.

Este método toma la cadena que es "sub" y comprueba si es igual a las subcadenas de la cadena contenedor que son iguales en longitud a la "sub". Si nos fijamos en el bucle for, verá que itera en subcadenas (que son la longitud del "sub") sobre el cadena contenedor.

Cada iteración comprueba si la subcadena de la cadena contenedor es equalsIgnoreCase al sub.

 0
Author: seth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-30 16:53:23

Si tiene que buscar una cadena ASCII en otra cadena ASCII, como una URL , encontrará que mi solución es mejor. He probado el método de icza y el mío para la velocidad y aquí están los resultados:

  • Caso 1 tomó 2788 ms-regionMatches
  • Caso 2 tomó 1520 ms - my

El código:

public static String lowerCaseAscii(String s) {
    if (s == null)
        return null;

    int len = s.length();
    char[] buf = new char[len];
    s.getChars(0, len, buf, 0);
    for (int i=0; i<len; i++) {
        if (buf[i] >= 'A' && buf[i] <= 'Z')
            buf[i] += 0x20;
    }

    return new String(buf);
}

public static boolean containsIgnoreCaseAscii(String str, String searchStr) {
    return StringUtils.contains(lowerCaseAscii(str), lowerCaseAscii(searchStr));
}
 0
Author: Revertron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-30 17:02:52
import java.text.Normalizer;

import org.apache.commons.lang3.StringUtils;

public class ContainsIgnoreCase {

    public static void main(String[] args) {

        String in = "   Annulée ";
        String key = "annulee";

        // 100% java
        if (Normalizer.normalize(in, Normalizer.Form.NFD).replaceAll("[\\p{InCombiningDiacriticalMarks}]", "").toLowerCase().contains(key)) {
            System.out.println("OK");
        } else {
            System.out.println("KO");
        }

        // use commons.lang lib
        if (StringUtils.containsIgnoreCase(Normalizer.normalize(in, Normalizer.Form.NFD).replaceAll("[\\p{InCombiningDiacriticalMarks}]", ""), key)) {
            System.out.println("OK");
        } else {
            System.out.println("KO");
        }

    }

}
 0
Author: sgrillon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-09 13:24:46
String x="abCd";
System.out.println(Pattern.compile("c",Pattern.CASE_INSENSITIVE).matcher(x).find());
 -1
Author: IVY,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-03 12:20:09

Simplemente podrías hacer algo como esto:

String s1 = "AbBaCca";
String s2 = "bac";
String toLower = s1.toLowerCase();
return toLower.contains(s2);
 -1
Author: Erick Kondela,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-03 15:44:37

Otro método fácil de usar para encontrar una cadena dentro de una cadena es STRING.INDEXOF ()

  String str = new String("Welcome");
  System.out.print("Found Index :" );
  System.out.println(str.indexOf( 'o' ));

Índice encontrado: 4

Www.tutorialspoint.com/java/java_string_indexof.htm

 -1
Author: rainer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-22 11:04:51
"AbCd".toLowerCase().contains("abcD".toLowerCase())
 -1
Author: Takhir Atamuratov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-13 09:07:50