it-swarm-eu.dev

Wie überprüfe ich, ob ein String in Java einen anderen String enthält?

Angenommen, ich habe zwei Saiten,

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

Ich möchte eine Überprüfung durchführen, die zurückgibt, dass s2 In s1 Enthalten ist. Das kann ich machen mit:

return s1.contains(s2);

Ich bin mir ziemlich sicher, dass bei contains() zwischen Groß- und Kleinschreibung unterschieden wird, kann dies jedoch nicht sicher anhand der Dokumentation feststellen. Wenn es dann ist, würde meine beste Methode ungefähr sein:

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

Abgesehen davon, gibt es eine andere (möglicherweise bessere) Möglichkeit, dies ohne Berücksichtigung der Groß- und Kleinschreibung zu erreichen?

359
Aaron

Ja, Groß- und Kleinschreibung wird berücksichtigt. Sie können Java.util.regex.Pattern mit dem CASE_INSENSITIVE-Flag für Übereinstimmungen ohne Berücksichtigung der Groß-/Kleinschreibung verwenden:

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

EDIT: Wenn s2 reguläre Sonderzeichen enthält (von denen es viele gibt), ist es wichtig, diese zuerst anzuführen. Ich habe meine Antwort korrigiert, da es die erste ist, die die Leute sehen werden, aber stimmen Sie für die von Matt Quail, seit er darauf hingewiesen hat.

301
Dave L.

Ein Problem mit die Antwort von Dave L. ist, wenn s2 Regex-Markups wie \d Usw. enthält.

Sie möchten Pattern.quote () auf s2 aufrufen:

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

Sie können verwenden

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

Die Apache Commons Bibliothek ist sehr nützlich für diese Art von Dingen. Und dieser spezielle Ausdruck ist möglicherweise besser als reguläre Ausdrücke, da reguläre Ausdrücke in Bezug auf die Leistung immer teuer sind.

141
muhamadto

Eine schnellere Implementierung: Verwenden von String.regionMatches()

Die Verwendung von regulären Ausdrücken kann relativ langsam sein. Es spielt keine Rolle, ob Sie nur in einem Fall nachsehen möchten. Aber wenn Sie ein Array oder eine Sammlung von Tausenden oder Hunderttausenden von Zeichenfolgen haben, kann es ziemlich langsam werden.

Die hier vorgestellte Lösung verwendet weder reguläre Ausdrücke noch toLowerCase() (was ebenfalls langsam ist, da sie weitere Zeichenfolgen erstellt und diese nach der Überprüfung einfach wegwirft).

Die Lösung baut auf der Methode String.regionMatches () auf, die unbekannt zu sein scheint. Es wird geprüft, ob 2 String Regionen übereinstimmen. Wichtig ist jedoch, dass es auch eine Überladung mit einem praktischen ignoreCase Parameter gibt.

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

Geschwindigkeitsanalyse

Diese Geschwindigkeitsanalyse ist keine Hexerei, sondern nur ein grobes Bild der Geschwindigkeit der verschiedenen Methoden.

Ich vergleiche 5 Methoden.

  1. Unsere enthält die IgnoreCase () -Methode.
  2. Durch Konvertieren beider Zeichenfolgen in Kleinbuchstaben und Aufrufen von String.contains().
  3. Durch Konvertieren der Quellzeichenfolge in Kleinbuchstaben und Aufrufen von String.contains() mit der vorab zwischengespeicherten Unterzeichenfolge in Kleinbuchstaben. Diese Lösung ist bereits nicht so flexibel, da sie eine vordefinierte Teilzeichenfolge testet.
  4. Verwendung eines regulären Ausdrucks (die akzeptierte Antwort Pattern.compile().matcher().find()...)
  5. Verwenden von regulären Ausdrücken, jedoch mit vorab erstellten und zwischengespeicherten Pattern. Diese Lösung ist bereits nicht so flexibel, da sie eine vordefinierte Teilzeichenfolge testet.

Ergebnisse (durch 10-Millionen-maliges Aufrufen der Methode):

  1. Unsere Methode: 670 ms
  2. 2x toLowerCase () und enthält (): 2829 ms
  3. 1x toLowerCase () und enthält () mit zwischengespeicherter Teilzeichenfolge: 2446 ms
  4. Regexp: 7180 ms
  5. Regexp with cached 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 zur Kleinschreibung und Verwendung von contains(), 10x schneller im Vergleich zur Verwendung von regulären Ausdrücken und auch 3x schneller , selbst wenn Pattern vorab zwischengespeichert ist (und an Flexibilität verliert nach einem beliebigen Teilstring suchen).


Analyse-Testcode

Wenn Sie interessiert sind, wie die Analyse durchgeführt wurde, finden Sie hier die vollständige ausführbare 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");
    }

}
111
icza

Eine einfachere Möglichkeit, dies zu tun (ohne sich um die Mustererkennung kümmern zu müssen), wäre, beide Strings in Kleinbuchstaben umzuwandeln:

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

Ja, das ist erreichbar:

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;

Dieser Code gibt den String "TRUE!" als es herausfand, dass deine Charaktere enthalten waren.

16
Bilbo Baggins

Sie können reguläre Ausdrücke verwenden, und es funktioniert:

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

Ich habe einen Test durchgeführt, bei dem die Groß- und Kleinschreibung einer Zeichenfolge nicht berücksichtigt wurde. Ich habe einen Vektor von 150.000 Objekten, alle mit einem String als ein Feld, und wollte die Teilmenge finden, die einem String entspricht. Ich habe drei Methoden ausprobiert:

  1. Wandle alle in Kleinbuchstaben um

    for (SongInformation song: songs) {
        if (song.artist.toLowerCase().indexOf(pattern.toLowercase() > -1) {
                ...
        }
    }
    
  2. Verwenden Sie die Methode String matches ()

    for (SongInformation song: songs) {
        if (song.artist.matches("(?i).*" + pattern + ".*")) {
        ...
        }
    }
    
  3. Verwenden Sie reguläre Ausdrücke

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

Timing-Ergebnisse sind:

  • Kein Matchversuch: 20 ms

  • Zum Verringern der Übereinstimmung: 182 ms

  • Streichhölzer: 278 ms

  • Regulärer Ausdruck: 65 ms

Der reguläre Ausdruck scheint für diesen Anwendungsfall der schnellste zu sein.

3
Jan Newmarch

Hier sind einige Unicode-freundliche, die Sie erstellen können, wenn Sie ICU4j aufrufen. Ich denke, "Groß- und Kleinschreibung ignorieren" ist für die Methodennamen fraglich, da Primärstärkenvergleiche Groß- und Kleinschreibung zwar ignorieren, die Besonderheiten jedoch vom Gebietsschema abhängen. Aber es hängt hoffentlich von der Ländereinstellung ab, wie es der Benutzer erwartet.

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

Ich bin nicht sicher, was Ihre Hauptfrage hier ist, aber ja, .contains unterscheidet zwischen Groß- und Kleinschreibung.

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

Wir können Stream mit anyMatch verwenden und enthalten 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;
}

Grundsätzlich ist es eine Methode, die zwei Zeichenfolgen benötigt. Es soll sich um eine Version von contain () handeln, bei der die Groß- und Kleinschreibung nicht beachtet wird. Wenn Sie die Methode includes verwenden, möchten Sie sehen, ob eine Zeichenfolge in der anderen enthalten ist.

Diese Methode verwendet die Zeichenfolge "sub" und prüft, ob sie den Teilzeichenfolgen der Containerzeichenfolge entspricht, deren Länge der Zeichenfolge "sub" entspricht. Wenn Sie sich die for -Schleife ansehen, werden Sie feststellen, dass sie in Teilzeichenfolgen (das ist die Länge der "Unterzeichenfolge") über die Containerzeichenfolge iteriert.

Bei jeder Iteration wird geprüft, ob die Teilzeichenfolge der Containerzeichenfolge equalsIgnoreCase für die Unterzeichenfolge ist.

0
seth

Wenn Sie eine ASCII Zeichenfolge in einer anderen ASCII Zeichenfolge suchen müssen, z. B. eine [~ # ~] url [ ~ # ~] , Sie werden feststellen, dass meine Lösung besser ist. Ich habe die Methode von icza und meine auf die Geschwindigkeit getestet und hier sind die Ergebnisse:

  • Fall 1 dauerte 2788 ms - regionMatches
  • Fall 2 dauerte 1520 ms

Der 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
Revertron

Es gibt einen einfachen, übersichtlichen Weg, Regex-Flag zu verwenden (Groß- und Kleinschreibung wird nicht berücksichtigt {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

oder Sie können einen einfachen Ansatz verwenden und einfach die Groß- und Kleinschreibung der Zeichenfolge in die Groß- und Kleinschreibung der Teilzeichenfolge konvertieren und dann die Methode includes verwenden.

0
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