465 Stimmen

Wie kann man in Java prüfen, ob eine Zeichenkette eine andere Zeichenkette enthält, ohne die Groß- und Kleinschreibung zu berücksichtigen?

Angenommen, ich habe zwei Strings,

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

Ich möchte eine Prüfung durchführen, die Folgendes zurückgibt s2 ist enthalten in s1 . Ich kann dies mit tun:

return s1.contains(s2);

Ich bin mir ziemlich sicher, dass contains() zwischen Groß- und Kleinschreibung unterscheidet, aber ich kann das aus der Dokumentation nicht mit Sicherheit herauslesen. Wenn es ist, dann nehme ich an, meine beste Methode wäre etwas wie:

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

Abgesehen davon, gibt es eine andere (möglicherweise bessere) Möglichkeit, dies zu erreichen, ohne sich um die Groß- und Kleinschreibung zu kümmern?

353voto

Dave L. Punkte 42559

Ja, bei "enthält" wird zwischen Groß- und Kleinschreibung unterschieden. Sie können java.util.regex.Pattern mit dem Flag CASE_INSENSITIVE verwenden, um die Groß- und Kleinschreibung zu berücksichtigen:

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

EDITAR: Wenn s2 Regex-Sonderzeichen enthält (wovon es viele gibt), ist es wichtig, sie zuerst zu zitieren. Ich habe meine Antwort korrigiert, da sie die erste ist, die die Leute sehen werden, aber stimme für die von Matt Quail, da er darauf hingewiesen hat.

293voto

Matt Quail Punkte 6059

Ein Problem mit die Antwort von Dave L. ist, wenn s2 eine Regex-Auszeichnung wie \d , usw.

Sie wollen Pattern.quote() auf s2 aufrufen:

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

222voto

muhamadto Punkte 2330

Sie können verwenden

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

El Apache Commons Bibliothek ist für diese Art von Dingen sehr nützlich. Und diese spezielle kann besser sein als reguläre Ausdrücke als regex ist immer teuer in Bezug auf Leistung.

144voto

icza Punkte 335148

Eine schnellere Umsetzung: Die Nutzung von String.regionMatches()

Die Verwendung von regexp kann relativ langsam sein. Wenn Sie nur in einem Fall prüfen wollen, macht das nichts aus. Aber wenn Sie ein Array oder eine Sammlung von Tausenden oder Hunderttausenden von Zeichenfolgen haben, kann es ziemlich langsam werden.

Die unten vorgestellte Lösung verwendet weder reguläre Ausdrücke noch toLowerCase() (was ebenfalls langsam ist, da es weitere Zeichenketten erstellt und diese nach der Prüfung einfach wegwirft).

Die Lösung baut auf dem String.regionMatches() Methode, die unbekannt zu sein scheint. Sie prüft, ob 2 String Regionen übereinstimmen, aber was wichtig ist, ist, dass es auch eine Überlastung mit einer praktischen 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;
}

Analyse der Geschwindigkeit

Diese Geschwindigkeitsanalyse soll keine Raketenwissenschaft sein, sondern nur ein grobes Bild davon vermitteln, wie schnell die verschiedenen Methoden sind.

Ich vergleiche 5 Methoden.

  1. Unser enthältKleinschreibung ignorieren() Methode.
  2. Durch Konvertierung beider Zeichenketten in Kleinbuchstaben und Aufruf von String.contains() .
  3. Durch Konvertierung der Quellzeichenkette in Kleinbuchstaben und Aufruf von String.contains() mit der vorab zwischengespeicherten, kleingeschriebenen Teilzeichenkette. Diese Lösung ist schon deshalb nicht so flexibel, weil sie eine vordefinierte Teilzeichenkette testet.
  4. Mit regulärem Ausdruck (die akzeptierte Antwort Pattern.compile().matcher().find() ...)
  5. Verwendung regulärer Ausdrücke, aber mit vorab erstellten und zwischengespeicherten Pattern . Diese Lösung ist schon deshalb nicht so flexibel, weil sie einen vordefinierten Teilstring testet.

Ergebnisse (durch 10 Millionen Maliges Aufrufen der Methode):

  1. Unsere Methode: 670 ms
  2. 2x toLowerCase() und contains(): 2829 ms
  3. 1x toLowerCase() und contains() mit zwischengespeicherter Teilzeichenkette: 2446 ms
  4. Regexp: 7180 ms
  5. Regexp mit zwischengespeicherten Pattern : 1845 ms

Ergebnisse in einer Tabelle:

                                            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

Unsere Methode ist 4x schneller im Vergleich zu Kleinbuchstaben und der Verwendung von contains() , 10x schneller im Vergleich zur Verwendung regulärer Ausdrücke und auch 3x schneller auch wenn die Pattern zwischengespeichert wird (und die Flexibilität der Prüfung auf eine beliebige Teilzeichenkette verloren geht).


Analyse Test Code

Wenn es Sie interessiert, wie die Analyse durchgeführt wurde, finden Sie hier die vollständige lauffähige Anwendung:

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

}

41voto

Phil Punkte 35009

Eine einfachere Möglichkeit, dies zu tun (ohne sich um den Musterabgleich zu kümmern), wäre die Umwandlung beider String s zu Kleinbuchstaben:

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X