it-swarm.it

Come verificare se una stringa contiene un'altra stringa senza distinzione tra maiuscole e minuscole in Java?

Dì che ho due corde,

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

Voglio eseguire un controllo restituendo che s2 è contenuto in s1. Posso farlo con:

return s1.contains(s2);

Sono abbastanza sicuro che contains() rilevi la distinzione tra maiuscole e minuscole, tuttavia non è possibile determinarlo di sicuro leggendo la documentazione. Se è quindi suppongo che il mio metodo migliore sarebbe qualcosa di simile:

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

A parte questo, c'è un altro (possibilmente migliore) modo di farlo senza preoccuparsi della case-sensitive?

351
Aaron

Sì, contiene è case sensitive. È possibile utilizzare Java.util.regex.Pattern con il flag CASE_INSENSITIVE per la corrispondenza senza distinzione tra maiuscole e minuscole:

Pattern.compile(Pattern.quote(wantedStr), Pattern.CASE_INSENSITIVE).matcher(source).find();

EDIT: Se s2 contiene caratteri speciali regex (di cui ce ne sono molti) è importante citarlo prima. Ho corretto la mia risposta dal momento che è la prima che vedrà la gente, ma vota su Matt Quail da quando lo ha indicato.

300
Dave L.

Un problema con la risposta di Dave L. è quando s2 contiene un regex markup come \d, ecc.

Vuoi chiamare Pattern.quote () su s2:

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

Puoi usare

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

La libreria Apache Commons è molto utile per questo genere di cose. E questo particolare potrebbe essere migliore delle espressioni regolari in quanto le regex sono sempre costose in termini di prestazioni.

136
muhamadto

Un'implementazione più veloce: utilizzo di String.regionMatches()

L'uso di regexp può essere relativamente lento. È (essere lenti) non importa se vuoi solo controllare in un caso. Ma se hai una matrice o una collezione di migliaia o centinaia di migliaia di stringhe, le cose possono diventare piuttosto lente.

La soluzione presentata qui sotto non usa espressioni regolari né toLowerCase() (che è anche lento perché crea altre stringhe e le getta via dopo il controllo).

La soluzione si basa sul metodo String.regionMatches () che sembra essere sconosciuto. Controlla se 2 regioni Stringcorrispondono, ma ciò che è importante è che ha anche un sovraccarico con un pratico parametro ignoreCasename__.

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

Analisi della velocità

Questa analisi della velocità non significa essere scienza missilistica, solo un quadro approssimativo della velocità con cui i diversi metodi sono.

Confronto 5 metodi.

  1. Il nostro metodo containsIgnoreCase () .
  2. Convertendo entrambe le stringhe in lettere minuscole e chiamando String.contains().
  3. Convertendo la stringa sorgente in lettere minuscole e chiamando String.contains() con la sottostringa pre-cache, con il sottotitolo inferiore. Questa soluzione non è già così flessibile perché verifica una sottostringa predefiede.
  4. Usando l'espressione regolare (la risposta accettata Pattern.compile().matcher().find()...)
  5. Uso dell'espressione regolare ma con Patternpre-creato e memorizzato nella cache. Questa soluzione non è già così flessibile perché verifica una sottostringa predefinita.

Risultati (chiamando il metodo 10 milioni di volte):

  1. Il nostro metodo: 670 ms
  2. 2x toLowerCase () e contains (): 2829 ms
  3. 1x toLowerCase () e contains () con sottostringa memorizzata nella cache: 2446 ms
  4. Regexp: 7180 ms
  5. Regexp con cache Patternname__: 1845 ms

Risultati in una tabella:

                                            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

Il nostro metodo è 4x più veloce rispetto al minuscolo e usa contains(), 10 volte più veloce rispetto all'utilizzo di espressioni regolari e anche 3 volte più veloce anche se Patternè pre-memorizzato nella cache (e perde la flessibilità del controllo di una sottostringa arbitraria).


Codice del test di analisi

Se sei interessato a come è stata eseguita l'analisi, ecco l'applicazione eseguibile 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");
    }

}
107
icza

Un modo più semplice per farlo (senza preoccuparsi della corrispondenza del modello) sarebbe la conversione di Strings in minuscolo:

String foobar = "fooBar";
String bar = "FOO";
if (foobar.toLowerCase().contains(bar.toLowerCase()) {
    System.out.println("It's a match!");
}
18
Phil

Sì, questo è realizzabile:

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;

Questo codice restituirà la stringa "VERO!" come ha scoperto che i tuoi personaggi erano contenuti.

16
Bilbo Baggins

Puoi usare espressioni regolari , e funziona:

boolean found = s1.matches("(?i).*" + s2+ ".*");
6
Shiv

Ho fatto un test per trovare una corrispondenza insensibile alla distinzione tra maiuscole e minuscole di una stringa. Ho un vettore di 150.000 oggetti tutti con una stringa come un campo e volevo trovare il sottoinsieme che corrisponde a una stringa. Ho provato tre metodi:

  1. Converti tutto in minuscolo

    for (SongInformation song: songs) {
        if (song.artist.toLowerCase().indexOf(pattern.toLowercase() > -1) {
                ...
        }
    }
    
  2. Utilizzare il metodo String matches ()

    for (SongInformation song: songs) {
        if (song.artist.matches("(?i).*" + pattern + ".*")) {
        ...
        }
    }
    
  3. Usa le espressioni regolari

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

I risultati del cronometraggio sono:

  • Nessuna corrispondenza tentata: 20 msec

  • Abbassare la corrispondenza: 182 msecs

  • Corrispondenze per stringhe: 278 msec

  • Espressione regolare: 65 msec

L'espressione regolare sembra essere la più veloce per questo caso d'uso.

3
Jan Newmarch

Ecco alcuni esempi compatibili con Unicode che puoi eseguire se inserisci ICU4j. Immagino che "ignorare il caso" sia discutibile per i nomi dei metodi, anche se i confronti di forza primaria ignorano il caso, è descritto come le specifiche essendo dipendenti dalla locale. Ma si spera che dipenda dalle impostazioni locali in un modo che l'utente si aspetterebbe.

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
Trejkaz

Non sono sicuro di quale sia la tua domanda principale qui, ma sì, .contains è case sensitive.

1
SCdF
"AbCd".toLowerCase().contains("abcD".toLowerCase())
1

Possiamo usare lo streaming con anyMatch e contiene Java 8

public class Test2 {
    public static void main(String[] args) {

        String a = "Gina Gini Protijayi Soudipta";
        String b = "Gini";

        System.out.println(WordPresentOrNot(a, b));
    }// main

    private static boolean WordPresentOrNot(String a, String b) {
    //contains is case sensitive. That's why change it to upper or lower case. Then check
        // Here we are using stream with anyMatch
        boolean match = Arrays.stream(a.toLowerCase().split(" ")).anyMatch(b.toLowerCase()::contains);
        return match;
    }

}
0
Soudipta Dutta
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;
}

Fondamentalmente, è un metodo che prende due stringhe. Si suppone che sia una versione non sensibile di contiene (). Quando si utilizza il metodo contiene, si desidera vedere se una stringa è contenuta nell'altra.

Questo metodo accetta la stringa "sub" e controlla se è uguale alle sottostringhe della stringa del contenitore che sono uguali in lunghezza al "sub". Se si guarda il ciclo for, si vedrà che itera in sottostringhe (che sono la lunghezza del "sub") sulla stringa del contenitore.

Ogni iterazione verifica se la sottostringa della stringa del contenitore è equalsIgnoreCase nel sub.

0
seth

Se devi cercare una stringa ASCII in un'altra stringa ASCII, ad esempio URL , troverai la mia soluzione per essere migliore. Ho testato il metodo di icza e il mio per la velocità e qui ci sono i risultati:

  • Il caso 1 ha richiesto 2788 ms - regionMatch
  • Il caso 2 ha richiesto 1520 ms - il mio

Il codice:

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
Revertron

C'è un modo semplice e conciso, usando il flag regex (case insensitive {i}):

 String s1 = "hello abc efg";
 String s2 = "ABC";
 s1.matches(".*(?i)"+s2+".*");

/*
 * .*  denotes every character except line break
 * (?i) denotes case insensitivity flag enabled for s2 (String)
 * */
0
Mr.Q
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
sgrillon