Comment vérifier si une chaîne de caractères contient une autre chaîne de caractères d'une manière non sensible à la casse en Java?

Dire que j'ai deux chaînes,

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

je veux effectuer un contrôle retournant que s2 est contenu dans s1 . Je peux le faire avec:

return s1.contains(s2);

je suis assez sûr que contains() est sensible à la casse, mais je ne peux pas le déterminer avec certitude en lisant la documentation. Si c'est le cas, je suppose que ma meilleure méthode serait quelque chose comme:

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

tout cela mis à part, y a-t-il un autre (peut-être mieux) façon d'accomplir ceci sans se soucier de cas-sensibilité?

313
demandé sur Tunaki 2008-09-17 23:37:32

17 réponses

Oui, contient est sensible à la casse. Vous pouvez utiliser java.util.regex.Modèle avec les CASE_INSENSITIVE drapeau de correspondance insensible à la casse:

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

EDIT: Si s2 contient des regex caractères spéciaux (qui sont nombreuses), il est important de citer en premier. J'ai corrigé ma réponse car c'est la première que les gens verront, mais votez pour Matt Quail depuis qu'il l'a fait remarquer.

281
répondu Dave L. 2008-09-27 22:21:48

Un problème avec les la réponse de Dave L. , c'est quand s2 contient des regex de balisage tels que \d , etc.

vous voulez appeler Pattern.quote() sur s2:

Pattern.compile(Pattern.quote(s2), Pattern.CASE_INSENSITIVE).matcher(s1).find();
232
répondu Matt Quail 2017-05-23 12:18:26

vous pouvez utiliser

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

la bibliothèque Apache Commons est très utile pour ce genre de choses. Et celle-ci peut être meilleure que les expressions régulières car regex coûte toujours cher en termes de performances.

122
répondu muhamadto 2015-10-30 16:42:31

Une Mise En Œuvre Plus Rapide: En Utilisant String.regionMatches()

utilisant regexp peut être relativement lent. (La lenteur) n'a pas d'importance si vous voulez juste pour vérifier dans un cas. Mais si vous avez un tableau ou une collection de milliers ou de centaines de milliers de cordes, les choses peuvent devenir assez lente.

la solution présentée ci-dessous n'utilise pas d'expressions régulières ni toLowerCase() (qui est également lent parce qu'il crée une autre chaîne et jette juste loin après le vérifier).

la solution s'appuie sur la chaîne de caractères .regionMatches () méthode qui semble inconnue. Il vérifie si les régions 2 String correspondent, mais ce qui est important c'est qu'il a aussi une surcharge avec un paramètre pratique 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;
}

Analyse De Vitesse

cette analyse de vitesse ne signifie pas être une science de la fusée, juste une image grossière de la rapidité des différentes méthodes.

je compare 5 méthodes.

  1. notre méthode de la containsIgnoreCase () .
  2. en convertissant les deux chaînes en minuscules et appeler String.contains() .
  3. en convertissant la chaîne de caractères source en minuscules et en appelant String.contains() avec la chaîne de caractères pré-mise en cache, plus basse. Cette solution n'est déjà pas aussi flexible parce qu'elle teste un substring.
  4. utilisant l'expression régulière (la réponse acceptée Pattern.compile().matcher().find() ...)
  5. Utilisation d'une expression régulière, mais avec pré-créé et mis en cache Pattern . Cette solution n'est déjà pas aussi flexible car elle teste un substrat prédéfini.

Résultats (en appelant la méthode 10 millions de fois):

  1. notre méthode: 670 ms
  2. 2x toLowerCase () et contient(): 2829 ms
  3. 1x toLowerCase() et() avec mise en cache de sous-chaîne: 2446 ms
  4. Regexp: 7180 ms
  5. Regexp avec mise en cache Pattern : 1845 ms

résultats dans un tableau:

                                            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

notre méthode est 4x plus rapide par rapport à la baisse et en utilisant contains() , 10x plus rapide par rapport à l'utilisation régulière les expressions et aussi 3x plus rapide même si le Pattern est pré-cache (et de perdre la flexibilité de vérifier pour un substrat arbitraire).


Code D'Analyse

si vous êtes intéressé par la façon dont l'analyse a été effectuée, voici l'application complète runnable:

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
répondu icza 2016-09-26 15:08:59

Oui, c'est possible:

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;

ce code renvoie la chaîne "TRUE!"comme il a trouvé que vos caractères étaient contenus.

17
répondu Bilbo Baggins 2017-01-03 21:10:21

une façon plus simple de faire cela (sans se soucier de l'appariement des motifs) serait de convertir à la fois String s en minuscules:

String foobar = "fooBar";
String bar = "FOO";
if (foobar.toLowerCase().contains(bar.toLowerCase()) {
    System.out.println("It's a match!");
}
15
répondu Phil 2012-01-16 17:29:04

vous pouvez utiliser expressions régulières , et cela fonctionne:

boolean found = s1.matches("(?i).*" + s2+ ".*");
6
répondu Shiv 2015-10-30 16:44:30

voici quelques Unicode-friendly ceux que vous pouvez faire si vous tirez dans ICU4j. Je suppose que "ignorer le cas" est discutable pour les noms de méthode parce que bien que les comparaisons de la force primaire ne tiennent pas compte du cas, il est décrit comme les détails étant dépendante locale. Mais il faut espérer que cela dépend de la localisation de la manière dont l'utilisateur s'y attend.

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
répondu Trejkaz 2014-05-27 14:29:18

j'ai fait un test pour trouver une correspondance non sensible à la casse d'une chaîne. J'ai un vecteur de 150.000 objets tous avec une chaîne comme un seul champ et je voulais trouver le sous-ensemble qui correspondait à une chaîne. J'ai essayé trois méthodes:

  1. Convertir toutes les minuscules

    for (SongInformation song: songs) {
        if (song.artist.toLowerCase().indexOf(pattern.toLowercase() > -1) {
                ...
        }
    }
    
  2. Utiliser la Chaîne de caractères correspond à() la méthode

    for (SongInformation song: songs) {
        if (song.artist.matches("(?i).*" + pattern + ".*")) {
        ...
        }
    }
    
  3. utiliser des expressions régulières

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

le moment les résultats sont les suivants:

  • Aucune tentative de mise en correspondance: 20 msecs

  • Pour faire baisser le match: 182 msecs

  • Chaîne correspond à: 278 msecs

  • Expression régulière: 65 msecs

l'expression régulière semble être la plus rapide pour ce cas d'utilisation.

2
répondu Jan Newmarch 2015-10-30 16:47:47

Je ne sais pas quelle est votre question principale, mais oui .contient est sensible à la casse.

1
répondu SCdF 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;
}

fondamentalement, c'est une méthode qui prend deux cordes. Il est supposé être une version non sensible à la casse de contains(). Lors de l'utilisation de la méthode contains, vous voulez voir si une chaîne est contenue dans l'autre.

cette méthode prend la chaîne qui est" sub "et vérifie si elle est égale aux sous-chaînes du conteneur qui sont égales en longueur au"sub". Si vous regardez la boucle for , vous verrez qu'elle itère en soustractions (qui sont la longueur du "sous") au-dessus de la chaîne du conteneur.

chaque itération vérifie si le substrat de la chaîne du conteneur est equalsIgnoreCase au sub.

0
répondu seth 2015-10-30 16:53:23

si vous devez rechercher une chaîne ASCII dans une autre chaîne ASCII, comme URL , vous trouverez ma solution pour être meilleur. J'ai testé la méthode d'icza et la mienne pour la vitesse et voici les résultats:

  • Cas 1 a pris 2788 ms - regionMatches
  • Cas 2 a pris 1520 ms - mon

le code:

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
répondu Revertron 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
répondu sgrillon 2016-11-09 13:24:46
String x="abCd";
System.out.println(Pattern.compile("c",Pattern.CASE_INSENSITIVE).matcher(x).find());
-1
répondu IVY 2012-12-03 12:20:09

vous pourriez simplement faire quelque chose comme ceci:

String s1 = "AbBaCca";
String s2 = "bac";
String toLower = s1.toLowerCase();
return toLower.contains(s2);
-1
répondu Erick Kondela 2015-12-03 15:44:37

une autre méthode facile à utiliser pour trouver une chaîne à l'intérieur d'une chaîne est la chaîne.Index de ()

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

Found Index :4

www.tutorialspoint.com/java/java_string_indexof.htm

-1
répondu rainer 2017-07-22 11:04:51
"AbCd".toLowerCase().contains("abcD".toLowerCase())
-1
répondu Takhir Atamuratov 2017-12-13 09:07:50