web-dev-qa-db-ja.com

Javaで大文字と小文字を区別しない方法でStringに別のStringが含まれているかどうかを確認する方法

2本の弦があるとします。

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

s2s1内に含まれていることを返すチェックを実行したいです。私はこれを行うことができます:

return s1.contains(s2);

私はcontains()が大文字と小文字を区別することを確信しています、しかし私はドキュメンテーションを読むことから確かにこれを決定することができません。もしそうなら、私の最善の方法は次のようになると思います。

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

これ以外にも、大文字と小文字の区別を気にせずにこれを達成するための別の(おそらくより良い)方法はありますか?

351
Aaron

はい、含むは大文字と小文字を区別します。大文字と小文字を区別しないで一致させるには、CASE_INSENSITIVEフラグを指定してJava.util.regex.Patternを使用できます。

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

EDIT:s2に正規表現の特殊文字(たくさんある)が含まれている場合は、最初に引用符を付けることが重要です。それが人々が最初に見るものであるので私は私の答えを修正しました、しかし彼がこれを指摘したのでマットクアイルのものを投票してください。

300
Dave L.

Dave L.による答え に関する問題の1つは、s2に\dなどの正規表現マークアップが含まれている場合です。

あなたはs2でPattern.quote()を呼び出したいです。

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

あなたが使用することができます

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

Apache Commons ライブラリはこのようなことに非常に便利です。正規表現はパフォーマンスの点で常に高価であるため、そしてこの特定のものは正規表現よりも良いかもしれません。

136
muhamadto

より速い実装:String.regionMatches()の利用

正規表現の使用は比較的遅くなる可能性があります。あなたがただ一つのケースをチェックインしたいのであれば、それは(遅い)問題ではありません。しかし、数千または数十万の文字列の配列またはコレクションがあると、処理がかなり遅くなる可能性があります。

以下に提示する解決策は正規表現もtoLowerCase()も使用しません(これは別の文字列を作成し、チェックの後にそれらを捨てるだけなので遅いです)。

解決策は String.regionMatches() メソッドに基づいていますが、不明のようです。 2つのString領域が一致するかどうかをチェックしますが、重要なことは便利なignoreCaseパラメータを持つオーバーロードもあるということです。

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

スピード解析

この速度分析は、ロケット科学であることを意味するのではなく、さまざまな方法がいかに速いかの大まかな図にすぎません。

5つの方法を比較します。

  1. 私たちのcontainsIgnoreCase()メソッド。
  2. 両方の文字列を小文字に変換してString.contains()を呼び出します。
  3. ソース文字列を小文字に変換し、プリキャッシュされた小文字の部分文字列でString.contains()を呼び出します。このソリューションは、事前定義されたサブストリングをテストするため、すでにそれほど柔軟ではありません。
  4. 正規表現を使用する(公認回答Pattern.compile().matcher().find()...)
  5. 正規表現を使用しますが、事前に作成されキャッシュされたPatternを使用します。この解決策は、事前定義された部分文字列をテストするため、すでにそれほど柔軟ではありません。

結果(メソッドを1000万回呼び出すことによって):

  1. 私たちの方法:670 ms
  2. toLowerCase()の2倍の値():2829ミリ秒
  3. キャッシュされた部分文字列を含む1倍のtoLowerCase()およびcontains():2446ミリ秒
  4. 正規表現:7180ミリ秒
  5. キャッシュされたPatternを持つ正規表現:1845 ms

テーブルの結果:

                                            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

私たちの方法は4倍速くcontains()を使って、10倍速いは/を使ってPatternが事前キャッシュされていても3倍高速(そして任意の部分文字列をチェックする柔軟性を失う)。


分析テストコード

分析がどのように行われたかに興味があるなら、これが完全に実行可能なアプリケーションです。

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

これを行うより簡単な方法は(パターンマッチングを気にせずに)両方のStringを小文字に変換することです。

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

はい、これは達成可能です:

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;

このコードは文字列 "TRUE!"を返します。それはあなたのキャラクターが含まれていることがわかったように。

16
Bilbo Baggins

あなたは 正規表現 を使うことができます、そしてそれは働きます:

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

大文字と小文字を区別しない文字列の一致を見つけるテストを行いました。 1つのフィールドとしてStringを持つ150,000のオブジェクトすべてのVectorがあり、ストリングに一致するサブセットを見つけたいと思いました。私は3つの方法を試しました:

  1. すべて小文字に変換

    for (SongInformation song: songs) {
        if (song.artist.toLowerCase().indexOf(pattern.toLowercase() > -1) {
                ...
        }
    }
    
  2. String matches()メソッドを使う

    for (SongInformation song: songs) {
        if (song.artist.matches("(?i).*" + pattern + ".*")) {
        ...
        }
    }
    
  3. 正規表現を使う

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

タイミング結果は次のとおりです。

  • 未遂試合:20ミリ秒

  • 低い試合へ:182ミリ秒

  • ストリング一致:278ミリ秒

  • 正規表現:65ミリ秒

このユースケースでは、正規表現が最も速いようです。

3
Jan Newmarch

これは、あなたがICU4jを利用すれば作ることができる、Unicodeにやさしいものです。一次強度の比較では大文字と小文字の区別は無視されますが、詳細はロケール依存であると説明されているため、「大文字と小文字の区別なし」はメソッド名には疑問があります。しかし、ユーザーが期待するようにロケール依存であることが望ましいです。

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

あなたの主な質問がここにあるかどうか私にはわかりませんが、はい、.containsは大文字と小文字を区別します。

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

AnyMatchでストリームを使用でき、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;
}

基本的には、2つの文字列を受け取るメソッドです。これはcontains()の大文字と小文字を区別しないバージョンと想定されています。 containsメソッドを使用するときは、一方の文字列がもう一方の文字列に含まれているかどうかを確認します。

このメソッドは、 "sub"である文字列を取得し、それが "sub"と同じ長さであるコンテナ文字列の部分文字列と等しいかどうかを確認します。 forループを見ると、コンテナ文字列の部分文字列( "sub"の長さ)を繰り返していることがわかります。

それぞれの繰り返しは、コンテナ文字列の部分文字列がsubに対してequalsIgnoreCaseであるかどうかを確認します。

0
seth

URL のように、別のASCII文字列でASCII文字列を検索する必要がある場合は、あなたは私の解決策がより良いことがわかります。私はスピードのためにiczaの方法と私のものをテストしましたそしてここに結果があります:

  • ケース1は2788ミリ秒かかりました - regionMatches
  • ケース2は1520ミリ秒かかりました - 私の

コード:

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

正規表現フラグを使用する簡単で簡潔な方法があります(大文字と小文字を区別しない{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

または、単純なアプローチを使用して、文字列の大文字小文字を部分文字列の大文字小文字に変換してから、containsメソッドを使用できます。

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