Hoe te controleren of een String een andere String bevat op een hoofdletterongevoelige manier in Java?

Stel dat ik twee strings heb,

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

Ik wil een controle uitvoeren om te laten zien dat s2zich in s1bevindt. Ik kan dit doen met:

return s1.contains(s2);

Ik ben er vrij zeker van dat contains()hoofdlettergevoelig is, maar ik kan dit niet met zekerheid vaststellen na het lezen van de documentatie. Als dat zo is, denk ik dat mijn beste methode zoiets zou zijn als:

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

Dit alles terzijde, is er een andere (mogelijk betere) manier om dit te bereiken zonder rekening te houden met hoofdlettergevoeligheid?


Antwoord 1, autoriteit 100%

Ja, bevat is hoofdlettergevoelig. U kunt java.util.regex.Pattern gebruiken met de CASE_INSENSITIVE-vlag voor hoofdletterongevoelige overeenkomsten:

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

BEWERK:Als s2 speciale regex-tekens bevat (waarvan er veel zijn), is het belangrijk om deze eerst te citeren. Ik heb mijn antwoord gecorrigeerd omdat dit het eerste is dat mensen zullen zien, maar stem op Matt Quail’s omdat hij hierop heeft gewezen.


Antwoord 2, autoriteit 82%

Eén probleem met het antwoord door Dave L.is wanneer s2 regex-opmaak bevat, zoals \d, enz.

Je wilt Pattern.quote() aanroepen op s2:

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

Antwoord 3, autoriteit 55%

U kunt

. gebruiken

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

De Apache Commons-bibliotheek is erg handig voor dit soort dingen. En deze specifieke is misschien beter dan reguliere expressies, omdat regex altijd duur is in termen van prestaties.


Antwoord 4, autoriteit 38%

Een snellere implementatie: gebruik van String.regionMatches()

Het gebruik van regexp kan relatief traag zijn. Het (langzaam) maakt niet uit of u slechts in één geval wilt inchecken. Maar als je een array of een verzameling van duizenden of honderdduizenden strings hebt, kan het behoorlijk langzaam gaan.

De onderstaande oplossing gebruikt geen reguliere expressies en ook geen toLowerCase()(wat ook traag is omdat het andere strings aanmaakt en deze na de controle gewoon weggooit).

De oplossing bouwt voort op de String.regionMatches()methode die onbekend lijkt. Het controleert of 2 String-regio’s overeenkomen, maar wat belangrijk is, is dat het ook overbelast is met een handige ignoreCase-parameter.

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

Snelheidsanalyse

Deze snelheidsanalyse is geen raketwetenschap, maar een ruw beeld van hoe snel de verschillende methoden zijn.

Ik vergelijk 5 methoden.

  1. Onze containsIgnoreCase()methode.
  2. Door beide tekenreeksen naar kleine letters te converteren en String.contains()aan te roepen.
  3. Door de brontekenreeks om te zetten in kleine letters en String.contains()aan te roepen met de vooraf in de cache opgeslagen subtekenreeks in kleine letters. Deze oplossing is al niet zo flexibel omdat het een predefiend substring test.
  4. Reguliere expressie gebruiken (het geaccepteerde antwoord Pattern.compile().matcher().find()…)
  5. Gebruik van reguliere expressie, maar met vooraf gemaakt en in de cache opgeslagen Pattern. Deze oplossing is al niet zo flexibel omdat het een vooraf gedefinieerde substring test.

Resultaten (door de methode 10 miljoen keer aan te roepen):

  1. Onze methode: 670 ms
  2. 2x toLowerCase() en bevat(): 2829 ms
  3. 1x toLowerCase() en bevat() met substring in cache: 2446 ms
  4. Regexp: 7180 ms
  5. Regexp met Patternin de cache: 1845 ms

Resultaten in een tabel:

                                           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

Onze methode is 4x snellervergeleken met kleine letters en het gebruik van contains(), 10x snellervergeleken met het gebruik van reguliere expressies en ook 3x sneller, zelfs als het Patternvooraf in de cache is opgeslagen (en de flexibiliteit verliest bij het controleren op een willekeurige substring).


Analyse testcode

Als u geïnteresseerd bent in hoe de analyse is uitgevoerd, vindt u hier de volledige uitvoerbare toepassing:

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

Antwoord 5, Autoriteit 9%

Een eenvoudigere manier om dit te doen (zonder zorgen te maken over patroonmatching) zou zowel Strings naar kleine letters converteren:

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

Antwoord 6, Autoriteit 5%

Ja, dit is haalbaar:

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;

Deze code retourneert de string “TRUE!” zoals het vaststelde dat je personages waren ingesloten.


Antwoord 7, Autoriteit 2%

U kunt reguliere uitdrukkingen , en het werkt:

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

Antwoord 8

Hier zijn enkele Unicode-vriendelijke die je kunt maken als je ICU4j erbij haalt. Ik denk dat “negeer case” twijfelachtig is voor de namen van de methoden, want hoewel vergelijkingen van de primaire sterkte case negeren, wordt het beschreven als de details die afhankelijk zijn van de locatie. Maar hopelijk is het landafhankelijk op een manier die de gebruiker zou verwachten.

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

Antwoord 9

Ik heb een test gedaan om een hoofdletterongevoelige overeenkomst van een tekenreeks te vinden. Ik heb een vector van 150.000 objecten, allemaal met een string als één veld en ik wilde de subset vinden die overeenkwam met een string. Ik heb drie methoden geprobeerd:

  1. Alles converteren naar kleine letters

    for (SongInformation song: songs) {
        if (song.artist.toLowerCase().indexOf(pattern.toLowercase() > -1) {
                ...
        }
    }
    
  2. Gebruik de methode String matches()

    for (SongInformation song: songs) {
        if (song.artist.matches("(?i).*" + pattern + ".*")) {
        ...
        }
    }
    
  3. Gebruik reguliere expressies

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

Timing-resultaten zijn:

  • Geen poging tot match: 20 msecs

  • Naar onderste match: 182 msecs

  • String-overeenkomsten: 278 msecs

  • Regelmatige expressie: 65 msecs

De reguliere expressie lijkt het snelst te zijn voor dit gebruik.


Antwoord 10

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

Antwoord 11

Er is een eenvoudige beknopte manier, met behulp van RGEX-vlag (zaak ongevoelig {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)
 * */

Antwoord 12

Een manier om het te doen om beide snaren naar lager of hoofdletters te converteren met behulp van Tolowercase () of Touppercase () -methoden en -test.

public class Sample {
   public static void main(String args[]){
      String str = "Hello Welcome to insensitive Container";
      String test = "Java Testing";
      Boolean bool = str.toLowerCase().contains(test.toLowerCase());
      System.out.println(bool);
   }
}

Hier is een andere manier voor case ongevoelige matching met Java.Util.Regex.pattern met de Case_Sensitive Flag.

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

Antwoord 13

Ik weet niet zeker wat uw belangrijkste vraag hier is, maar ja,. Containes is hoofdlettergevoelig.


Antwoord 14

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

Kortom, het is een methode waarvoor twee strings nodig zijn. Het wordt verondersteld een niet-hoofdlettergevoelige versie van bevat() te zijn. Als je de methode bevat, wil je zien of de ene tekenreeks in de andere zit.

Deze methode neemt de tekenreeks die “sub” is en controleert of deze gelijk is aan de subtekenreeksen van de containerreeks die even lang zijn als de “sub”. Als je naar de for-lus kijkt, zul je zien dat deze itereert in substrings (dat is de lengte van de “sub”) over de containerstring.

Elke iteratie controleert of de subtekenreeks van de containertekenreeks equalsIgnoreCaseis voor de sub.


Antwoord 15

Als je een ASCII-tekenreeks in een andere ASCII-tekenreeks moet zoeken, zoals een URL, je zult merken dat mijn oplossing beter is. Ik heb de methode van icza en de mijne getest op snelheid en hier zijn de resultaten:

  • Case 1 duurde 2788 ms – regionMatches
  • Case 2 duurde 1520 ms – mijn

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

Antwoord 16

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

Antwoord 17

We kunnen stream gebruiken met anyMatch en bevat 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;
    }
}

Antwoord 18

of je kunt een eenvoudige benadering gebruiken en gewoon het hoofdlettergebruik van de tekenreeks converteren naar het hoofdlettergebruik van de subtekenreeks en vervolgens de methode bevat gebruiken.


Antwoord 19

String x="abCd";
System.out.println(Pattern.compile("c",Pattern.CASE_INSENSITIVE).matcher(x).find());

Antwoord 20

Je zou gewoon zoiets als dit kunnen doen:

String s1 = "AbBaCca";
String s2 = "bac";
String toLower = s1.toLowerCase();
return toLower.contains(s2);

Other episodes