it-swarm-id.com

Bagaimana cara memeriksa apakah suatu String berisi String lain dalam suatu case case insensitive in Java?

Katakanlah saya punya dua string,

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

Saya ingin melakukan pemeriksaan kembali bahwa s2 terkandung dalam s1. Saya bisa melakukan ini dengan:

return s1.contains(s2);

Saya cukup yakin bahwa contains() adalah case-sensitive, namun saya tidak dapat menentukan ini dengan pasti dari membaca dokumentasi. Jika demikian maka saya kira metode terbaik saya akan menjadi sesuatu seperti:

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

Selain itu, apakah ada cara lain (mungkin lebih baik) untuk mencapai hal ini tanpa memperhatikan sensitivitas huruf besar-kecil?

351
Aaron

Ya, berisi sensitif huruf. Anda dapat menggunakan Java.util.regex.Pattern dengan flag CASE_INSENSITIVE untuk pencocokan yang tidak sensitif huruf:

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

EDIT: Jika s2 berisi karakter khusus regex (yang jumlahnya banyak) penting untuk mengutip terlebih dahulu. Saya sudah mengoreksi jawaban saya karena ini adalah yang pertama kali dilihat orang, tetapi pilih Matt Quail sejak dia menunjukkan ini.

300
Dave L.

Satu masalah dengan jawabannya oleh Dave L. adalah ketika s2 berisi markup regex seperti \d, dll.

Anda ingin memanggil Pattern.quote () di s2:

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

Kamu bisa memakai

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

Pustaka Apache Commons sangat berguna untuk hal semacam ini. Dan yang satu ini mungkin lebih baik daripada ekspresi reguler karena regex selalu mahal dalam hal kinerja.

136
muhamadto

Implementasi Lebih Cepat: Memanfaatkan String.regionMatches()

Menggunakan regexp bisa relatif lambat. Itu (lambat) tidak masalah jika Anda hanya ingin memeriksa dalam satu kasing. Tetapi jika Anda memiliki array atau koleksi ribuan atau ratusan ribu string, semuanya bisa menjadi sangat lambat.

Solusi yang disajikan di bawah ini tidak menggunakan ekspresi reguler atau toLowerCase() (yang juga lambat karena menciptakan string lain dan hanya membuangnya setelah pemeriksaan).

Solusinya dibangun pada metode String.regionMatches () yang tampaknya tidak dikenal. Ia memeriksa apakah 2 daerah String cocok, tetapi yang penting adalah bahwa ia juga memiliki kelebihan dengan parameter ignoreCase yang praktis.

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

Analisis Kecepatan

Analisis kecepatan ini tidak berarti menjadi ilmu roket, hanya gambaran kasar tentang seberapa cepat berbagai metode tersebut.

Saya membandingkan 5 metode.

  1. Metode kami berisiIgnoreCase () .
  2. Dengan mengonversi kedua string ke huruf kecil dan memanggil String.contains().
  3. Dengan mengonversi string sumber menjadi huruf kecil dan memanggil String.contains() dengan substring yang lebih dulu di-cache, lebih rendah-casing. Solusi ini sudah tidak sefleksibel karena akan menguji substring yang sudah ditentukan sebelumnya.
  4. Menggunakan ekspresi reguler (jawaban yang diterima Pattern.compile().matcher().find()...)
  5. Menggunakan ekspresi reguler tetapi dengan Pattern yang telah dibuat sebelumnya dan di-cache. Solusi ini sudah tidak sefleksibel karena menguji substring yang telah ditentukan.

Hasil (dengan memanggil metode 10 juta kali):

  1. Metode kami: 670 ms
  2. 2x toLowerCase () dan berisi (): 2829 ms
  3. 1x toLowerCase () dan berisi () dengan substring di-cache: 2446 ms
  4. Regexp: 7180 ms
  5. Regexp dengan Pattern cache: 1845 ms

Hasil dalam 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

Metode kami adalah 4x lebih cepat dibandingkan dengan huruf kecil dan menggunakan contains(), 10x lebih cepat dibandingkan dengan menggunakan ekspresi reguler dan juga 3x lebih cepat bahkan jika Pattern sudah di-cache sebelumnya (dan kehilangan fleksibilitas untuk memeriksa substring yang sewenang-wenang).


Kode Uji Analisis

Jika Anda tertarik bagaimana analisis dilakukan, berikut ini adalah aplikasi runnable yang lengkap:

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

Cara yang lebih sederhana untuk melakukan ini (tanpa khawatir tentang pencocokan pola) akan mengubah kedua Strings menjadi huruf kecil:

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

Ya, ini bisa dicapai:

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;

Kode ini akan mengembalikan String "BENAR!" karena ditemukan bahwa karakter Anda terkandung.

16
Bilbo Baggins

Anda dapat menggunakan ekspresi reguler , dan berfungsi:

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

Saya melakukan tes menemukan kecocokan case-string yang tidak sensitif. Saya memiliki 150.000 objek Vector semua dengan String sebagai satu bidang dan ingin menemukan bagian yang cocok dengan string. Saya mencoba tiga metode:

  1. Konversi semua menjadi huruf kecil

    for (SongInformation song: songs) {
        if (song.artist.toLowerCase().indexOf(pattern.toLowercase() > -1) {
                ...
        }
    }
    
  2. Gunakan metode String cocok ()

    for (SongInformation song: songs) {
        if (song.artist.matches("(?i).*" + pattern + ".*")) {
        ...
        }
    }
    
  3. Gunakan ekspresi reguler

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

Hasil pengaturan waktu adalah:

  • Tidak ada percobaan yang dicoba: 20 msecs

  • Untuk menurunkan kecocokan: 182 msecs

  • Pencocokan string: 278 msecs

  • Ekspresi reguler: 65 msecs

Ekspresi reguler terlihat menjadi yang tercepat untuk use case ini.

3
Jan Newmarch

Berikut ini beberapa yang ramah Unicode yang dapat Anda buat jika menarik ICU4j. Saya kira "abaikan case" dipertanyakan untuk nama-nama metode karena walaupun perbandingan kekuatan primer abaikan case, itu digambarkan sebagai spesifik yang bergantung pada lokal. Tapi mudah-mudahan ini tergantung pada cara yang diharapkan pengguna.

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

Saya tidak yakin apa pertanyaan utama Anda di sini, tapi ya, .contains peka huruf besar-kecil.

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

Kita dapat menggunakan streaming dengan AnyMatch dan berisi 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;
}

Pada dasarnya, ini adalah metode yang membutuhkan dua string. Seharusnya versi yang tidak sensitif case berisi (). Saat menggunakan metode berisi, Anda ingin melihat apakah satu string terkandung dalam yang lain.

Metode ini mengambil string yang "sub" dan memeriksa apakah itu sama dengan substring dari string kontainer yang panjangnya sama dengan "sub". Jika Anda melihat loop for, Anda akan melihat bahwa itu berulang di substring (yang merupakan panjang "sub") di atas string wadah.

Setiap iterasi memeriksa untuk melihat apakah substring dari string kontainer equalsIgnoreCase ke sub.

0
seth

Jika Anda harus mencari string ASCII dalam string lain ASCII, seperti --- URL , Anda akan menemukan solusi saya menjadi lebih baik. Saya telah menguji metode icza dan menambang untuk kecepatan dan berikut hasilnya:

  • Kasus 1 mengambil 2788 ms - regionMatches
  • Kasus 2 mengambil 1520 ms - my

Kode:

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

Ada cara ringkas dan sederhana, menggunakan bendera regex (case case {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