it-swarm-tr.com

Bir String'in Java'da büyük / küçük harf duyarsız bir şekilde başka bir Dize içerip içermediğini kontrol etme

İki telim olduğunu söyle

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

s2 içinde s1 içinde bulunduğunu gösteren bir kontrol yapmak istiyorum. Bunu ile yapabilirim:

return s1.contains(s2);

contains() öğesinin büyük/küçük harfe duyarlı olduğundan eminim, ancak belgelerini okumaktan emin olamıyorum. Öyleyse, en iyi yöntemimin şöyle bir şey olacağını varsayalım:

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

Tüm bunlar bir yana, büyük/küçük harfe duyarlılığı önemsemeden bunu başarmanın başka (muhtemelen daha iyi) bir yolu var mı?

359
Aaron

Evet, büyük/küçük harf duyarlıdır. Küçük harf duyarsız eşleme için Java.util.regex.Pattern öğesini CASE_INSENSITIVE bayrağıyla kullanabilirsiniz:

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

EDIT: Eğer s2, regex özel karakterleri içeriyorsa (birçoğu vardır) önce alıntı yapmak önemlidir. Cevabımı düzelttim, çünkü ilk görecek olan kişi bu, ama Matt Quail'in oy kullandığını belirtti.

301
Dave L.

Dave L tarafından verilen cevap ile ilgili bir sorun, s2'nin \d, vb. Gibi regex işaretlemesi içermesidir.

S2'de Pattern.quote () işlevini çağırmak istiyorsunuz:

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

Kullanabilirsiniz

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

Apache Commons kütüphanesi bu tür şeyler için çok faydalıdır. Ve bu özel ifade, düzenli ifadelerden daha iyi olabilir çünkü regex her zaman performans açısından pahalıdır.

141
muhamadto

Daha Hızlı Bir Uygulama: String.regionMatches() Kullanarak

Regexp kullanmak nispeten yavaş olabilir. Sadece bir durumda kontrol etmek isteyip istemediğiniz önemli değildir. Ancak binlerce veya yüz binlerce dizeden oluşan bir diziniz veya koleksiyonunuz varsa, işler oldukça yavaşlayabilir.

Aşağıda sunulan çözüm normal ifadeler veya toLowerCase() kullanmaz (başka bir karakter dizisi oluşturduğundan ve sadece kontrol ettikten sonra fırlatıp attığı için yavaştır).

Çözüm, bilinmeyen gibi görünen String.regionMatches () yöntemine dayanmaktadır. 2 String bölgesinin eşleşip eşleşmediğini kontrol eder, ancak asıl önemli olan kullanışlı bir ignoreCase parametresiyle aşırı yüklenme göstermesidir.

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

Hız analizi

Bu hız analizi, roket bilimi anlamına gelmez, sadece farklı yöntemlerin ne kadar hızlı olduğuna dair kaba bir resimdir.

5 yöntemi karşılaştırıyorum.

  1. Bizim includeIgnoreCase () yöntemimiz.
  2. Her iki dizeyi de küçük harfe dönüştürerek String.contains() öğesini çağırın.
  3. Kaynak dizgiyi küçük harfe dönüştürerek ve önbelleklenmiş, küçük harfli alt dizeyle String.contains() işlevini çağırın. Bu çözüm zaten esnek değil, çünkü önceden hazırlanmış bir alt dize test ediyor.
  4. Normal ifadeyi kullanarak (kabul edilen cevap Pattern.compile().matcher().find()...)
  5. Düzenli ifade kullanarak ancak önceden oluşturulmuş ve önbelleğe alınmış Pattern. Bu çözüm zaten esnek değil çünkü önceden tanımlanmış bir alt dize test ediyor.

Sonuçlar (yöntemi 10 milyon kez çağırarak):

  1. Bizim yöntemimiz: 670 ms
  2. 2x toLowerCase () ve içerir (): 2829 ms
  3. 1x toLowerCase () ve önbelleğe alınmış alt dizeli () içeren (2446 msn)
  4. Regexp: 7180 ms
  5. Önbelleğe alınmış Pattern ile Regexp: 1845 ms

Tablodaki sonuçlar:

                                            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

Metodumuz 4x daha hızlı, küçük harflerle ve contains() işlevini kullanmaya kıyasla, 10x daha hızlı normal ifadeleri kullanmaya kıyasla ve ayrıca x daha hızlıPattern önceden - önbelleğe alınmış (ve keyfi bir alt dize denetleme esnekliğini kaybediyor).


Analiz Test Kodu

Analizin nasıl yapıldığını merak ediyorsanız, çalıştırılabilir tam uygulama:

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

Bunu yapmanın daha basit bir yolu (desen eşleştirmesi hakkında endişelenmeden) her iki Strings harfini küçük harfe dönüştürmek olacaktır:

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

Evet, bu başarılabilir:

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;

Bu kod "TRUE!" Dizesini döndürür. karakterlerinin içerdiğini buldu.

16
Bilbo Baggins

normal ifadeler kullanabilirsiniz ve işe yarar:

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

Bir dize büyük/küçük harf duyarlı bir eşleşme bulma testi yaptım. Tek bir alan olarak bir Dize içeren 150.000 nesneden oluşan bir Vektörüm var ve bir dizeyle eşleşen alt kümeyi bulmak istedim. Üç yöntem denedim:

  1. Tümünü küçük harfe dönüştür

    for (SongInformation song: songs) {
        if (song.artist.toLowerCase().indexOf(pattern.toLowercase() > -1) {
                ...
        }
    }
    
  2. String eşleşme () yöntemini kullanın

    for (SongInformation song: songs) {
        if (song.artist.matches("(?i).*" + pattern + ".*")) {
        ...
        }
    }
    
  3. Normal ifadeler kullanın

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

Zamanlama sonuçları:

  • Eşleşme denemesi yok: 20 msn

  • Eşleşmeyi azaltmak için: 182 msn

  • Dize eşleşmeleri: 278 msn

  • Düzenli ifade: 65 msn

Normal ifade, bu kullanım durumu için en hızlı gibi görünüyor.

3
Jan Newmarch

İşte ICU4j'yi çekerseniz yapabileceğiniz bazı Unicode dostu olanlar. Sanırım "görmezden gelme" yöntem adları için sorgulanabilir, çünkü birincil güç karşılaştırmaları durumu görmezden gelse de, yerel olarak bağımlı olan özellikler olarak tanımlanıyor. Ancak, kullanıcının umacağı şekilde yerel ayarlara bağımlıdır.

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

Asıl sorunuzun burada ne olduğundan emin değilim, ama evet. .Contain büyük/küçük harf duyarlıdır.

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

AnyMatch ile akışı kullanabiliriz ve Java 8 içeriyorsa

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

Temel olarak, iki dize alan bir yöntemdir. İnclude () 'nın küçük harfe duyarlı olmayan bir sürümü olması gerekiyordu. İnclude yöntemini kullanırken, diğerinde bir dize olup olmadığını görmek istersiniz.

Bu yöntem "sub" olan dizgiyi alır ve "sub" uzunluğuna eşit olan konteyner dizesinin alt dizgilerine eşit olup olmadığını kontrol eder. for döngüsüne bakarsanız, konteyner dizgisi üzerinde alt dizelerde ("alt" uzunluğunda) yinelendiğini görürsünüz.

Her yineleme, konteyner dizesinin alt dizgisinin altta equalsIgnoreCase olup olmadığını kontrol eder.

0
seth

Bir ASCII dizgisini başka bir ASCII dizesinde aramak zorunda kalırsanız, örneğin URL gibi, çözümümün daha iyi olduğunu göreceksiniz. İcza'nın metodunu ve benimki hızını test ettim ve işte sonuçları:

  • Dava 1 2788 ms aldı - regionMatches
  • Dava 2 1520 ms aldı - benim

Kod:

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

Regex bayrağını kullanarak basit ve özlü bir yol var (küçük harf duyarsız {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

veya basit bir yaklaşım kullanabilir ve sadece dizenin büyük harfini alt dizenin büyük harfine dönüştürebilir ve daha sonra include yöntemini kullanabilirsiniz.

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