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


386

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

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

s2含まれるチェック返却を実行したいs1。私はこれを行うことができます:

return s1.contains(s2);

contains()大文字と小文字が区別されることは確かですが、ドキュメントを読んでこれを確認することはできません。もしそうなら、私の最善の方法は次のようなものになると思います:

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

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


ドキュメントが失敗した場合、DrJavaはこれをテストする非常に簡単な方法です。[Interactions]ウィンドウにいくつかのテストケースを入力するだけで確認できます。
EfForEffort 2008

17
あなたはあなた自身の質問に答えたと思います。以下の解決策はこれより優れているとは思いません。しかし、彼らは間違いなく遅いです。
Nikolay Dimitrov

7
あなたの解決策は、答えの中のどれよりも単純です
LobsterMan '16

2
私とここの多くが探している答えはあなたの質問です。
Lalit Fauzdar 2017

1
あなたの例は、これを行うための最も単純で最も読みやすい、そしておそらく最良の方法です-私が見ているどの答えよりも優れています。
user1258361

回答:


320

はい、含むには大文字と小文字の区別があります。CASE_INSENSITIVEフラグを指定したjava.util.regex.Patternを使用して、大文字と小文字を区別しないマッチングを行うことができます。

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

編集: s2に正規表現の特殊文字(多数ある)が含まれている場合は、最初に引用することが重要です。回答が最初に表示されるので修正しましたが、マットクウェイルが指摘したことから投票します。


23
のドキュメントで述べられているようにPattern.CASE_INSENSITIVE、これはASCII文字に対してのみ機能します(つまり、「Ä」は「ä」と一致しません)。そのためには、UNICODE_CASEフラグを追加で指定する必要があります。
Philipp Wendler、2012年

72
このアプローチはPatternより高性能を使用していますs1.toLowerCase().contains(s2.toLowerCase())か?
Rajat Gupta

6
@ user01速度分析を行いました。:(私も速く解決策が示された)の結果を得るために私の答えを参照してくださいstackoverflow.com/a/25379180/1705598
icza

10
:それは私はより多くの私たちはより良い変数名があった場合に何が起こっていたかクリアするでしょうPattern.compile(Pattern.quote(needle), Pattern.CASE_INSENSITIVE).matcher(haystack).find()
ジョン・バウアーズ

5
@ user01の正確さはパフォーマンスよりも優先され、toLowerCaseを使用すると、誤った結果になる可能性があります(たとえば、同じ大文字の形式に小文字の形式が2つあるSigmaという文字を含む特定のギリシャ語のテキストを比較する場合)。
クリトスキリアクー

266

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

s2でPattern.quote()を呼び出します:

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

1
いいキャッチマット。どの方法がより効率的かを知りたいのですが-小文字が含まれている、またはパターンソリューション。パターンを使用すると、単一の比較では効率が低下しませんが、複数の比較では効率が向上しますか?
アーロン

41
.toLowerCase()。contains()メソッドは、ほとんどの場合、おそらく高速です。おそらく、複雑さを軽減するためにもそのスタイルを好むでしょう。
Matt Quail

3
@AaronFergusonはい、確かにtoLowerCase().contains()高速です。私はいくつかの速度分析を行いました。結果については私の答えを参照してください:stackoverflow.com/a/25379180/1705598
icza '19

2
@MattQuailが間違っている可能性がある場合、それが高速であっても意味がありません。たとえば、ギリシャ語の大文字のsigmaには2つの小文字の形式があり(単語の末尾に来るかどうかに応じて)、大文字と小文字を区別しない部分文字列の一致を行おうとすると、部分文字列がsigmaで終わるため、簡単に間違ってしまう可能性があります。結果。
クリトスキリアクー

私たちもPattern.UNICODE_CASEフラグを追加する必要があると思います。これを確認してもらえますか?
Thariq Nugrohotomo

160

使用できます

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

ApacheのCommonsのライブラリーは、この種のもののために非常に有用です。そして、正規表現は常にパフォーマンスの点で高価であるため、これは正規表現よりも優れている場合があります。


1
これがロケールを尊重するかどうか誰かが知っていますか?
Charles Wood

12
@CharlesWoodこれString.regionMatchesは、文字単位の変換を使用するに委譲するため、そうではありません。さらに、containsIgnoreCase("ß", "ss")-1を返しますが、これはすべてのロケールで間違っています(ドイツ語の「sharp s」は「ss」に大文字になります。)
maaartinus

それでは、ドイツ語の単語を比較する正しい方法はどれでしょうか。文字列を比較するすべての方法を複雑にする1つの言語のようです:P
chomp

1
ところで、ドイツ語は2017年に正式に大文字のßで拡張されました:de.wikipedia.org/wiki/Gro%C3%9Fes_%C3%9F。ドイツ語キーボードで、タイプは、Shift + Altキー+ GrのSS - >テスト:ẞ😁
Kawu

119

より高速な実装:活用 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. 2x toLowerCase()およびcontains():2829ミリ秒
  3. 1x toLowerCase()およびcontains()、キャッシュされたサブストリング:2446ミリ秒
  4. 正規表現:7180ミリ秒
  5. キャッシュ付きの正規表現Pattern:1845ミリ秒

テーブルの結果:

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

}

6
+1ですが、ß(ドイツ語の鋭いS、大文字のSS)と他の一部の文字(String.regionMatches両方の変換を試行するのソースを参照)では失敗することに注意してください。
maaartinus 2014年

2
あなたは常に同じ文字列をテストしますが、これは実際には公平な比較ではありません。「私は」は常に真ん中にあり、検索方法によって違いが生じる場合とそうでない場合があります。ランダムな文字列を生成し、部分文字列が存在しない場合の速度についてレポートすることもできます。

2
これは、Apache StringUtilsメソッドに非常に近いようです。grepcode.com
file

1
@ alain.janinm私は類似点を見ることができません。「近い」と思われる唯一のことStringUtils.containsIgnoreCase()は、私のソリューションとApacheの両方がregionMatches()(サイクルで)メソッドを使用することですが、それでも私が呼び出しString.regionMatches()たりApache を呼び出したりするのと同じではありませんCharSequenceUtils.regionMatches()
icza 2014年

2
@icza CharSequenceUtils.regionMatchesString.regionMatches実際に呼び出します。とにかく、私のポイントは情報を提供することでした。誰かが既にStringUtils libを使用している場合は、ベンチマークでそれを証明するような効率的な方法のように見えるので、彼はそれを呼び出すことができます。私がApache libを使用していなかった場合、私は
間違いなく

22

これを行うより簡単な方法(パターンマッチングを心配することなく)は、両方Stringのを小文字に変換します。

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

4
文字の大文字と小文字は言語に依存します。つまり、コンピュータでは機能しますが、顧客にとっては失敗します:)。@Adriaan Kosterのコメントを参照してください。
kroiz

1
@kroiz、それは文字列がどこから来たかに依存します。"foobar"と "FOO"の比較は常に一致しますが、ユーザー入力情報または言語固有のコンテンツを比較する場合は正しいです-開発者は注意する必要があります。
Phil

16

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

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!」を返します。あなたのキャラクターが含まれていることがわかりました。


12
toLowerCase()を使用する大きな欠点は、結果が現在のロケールに依存することです。参照:javapapers.com/core-java/…–
Adriaan Koster

4
この問題は小文字ではないため失敗するため、実際にはより良い解決策が含まれていますs2。このような詳細については話さないので、これはコンパイルされず、コンパイルされた場合は文字列が返されます。
maaartinus 2014年

6

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

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

3

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

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

  1. すべてを小文字に変換する

    for (SongInformation song: songs) {
        if (song.artist.toLowerCase().indexOf(pattern.toLowercase() > -1) {
                ...
        }
    }
  2. 文字列の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ミリ秒

この使用例では、正規表現が最も高速に見えます。


タイミングの結果を入れて良かったです。誰もが正規表現が遅いと言いますが、実際には、正規表現を1回コンパイルするだけで十分な場合は非常に高速です。
2017

1

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

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


0
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ループ、あなたはそれがコンテナ文字列の上に(「サブ」の長さ)のサブストリングに反復していることがわかります。

反復ごとに、コンテナ文字列equalsIgnoreCaseのサブ文字列がサブ文字列に対応しているかどうかが確認されます。


基本的には2つの文字列を取るメソッドです。それは、contains()の大文字小文字を区別しないバージョンであると想定されています。containsメソッドを使用する場合、一方の文字列がもう一方の文字列に含まれているかどうかを確認する必要があります。このメソッドは、「sub」である文字列を受け取り、それがコンテナ文字列の「sub」と同じ長さのサブ文字列と等しいかどうかを確認します。forループを見ると、コンテナ文字列に対してサブ文字列( "sub"の長さ)で反復していることがわかります。各反復は、コンテナ文字列のサブ文字列がサブ文字列と等しいかどうかを確認します。
セス

@あなたはおそらくあなたの答えにそれを追加する必要があります。
帽子をかぶった男

2
これはこれまでで最も遅い方法であり、ドイツ語でも失敗します。
maaartinus 2014年

0

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

    }

}

このコードスニペットをありがとうございます。このコードスニペットは、限られた短期間のヘルプを提供する場合があります。適切な説明は、なぜこれが問題の優れた解決策であるを示すことにより、長期的な価値を大幅に改善し、他の同様の質問を持つ将来の読者にとってより有用になるでしょう。答えを編集して、仮定を含めて説明を追加してください。
Toby Speight 2018

0
"AbCd".toLowerCase().contains("abcD".toLowerCase())

2
あなたのコードが問題をどのように解決するかを説明することによって、あなたの答えを改善できますか?
Isuka

1
この回答は、他の多くの人が提供したこの質問に対する他のより詳細な回答ですでに提案されています。この答えはここでは何の役にも立たないと思います。
DaveyDaveDave 2017

0

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

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


-1
String x="abCd";
System.out.println(Pattern.compile("c",Pattern.CASE_INSENSITIVE).matcher(x).find());

-1

あなたは単にこのようなことをすることができます:

String s1 = "AbBaCca";
String s2 = "bac";
String toLower = s1.toLowerCase();
return toLower.contains(s2);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.