indexOf大文字と小文字を区別しますか?


81

indexOf(String)メソッドでは大文字と小文字が区別されますか?もしそうなら、それの大文字と小文字を区別しないバージョンはありますか?


3
私が大物のパフォーマンスマンか何かであるというわけではありませんが(実際にはパフォーマンスチューニングは一種の悪だと思います)、. toUpperCaseは呼び出すたびに文字列をコピーするので、これをループで行う場合は、.toUpperCaseを移動してみてください可能であればループの。
ビルK

回答:


75

indexOf()方法は、すべて大文字と小文字が区別されます。文字列を事前に大文字/小文字に変換することで、大文字と小文字を区別しないようにすることができます(大まかに言えば、壊れた方法ですが、多くの場合に機能します)。

s1 = s1.toLowerCase(Locale.US);
s2 = s2.toLowerCase(Locale.US);
s1.indexOf(s2);

4
toUpperCaseを使用するときは、国際化の問題(つまり、トルコ語のİ)に注意してください。より適切な解決策は、str.toUpperCase(Locale.US).indexOf(...);を使用することです。
James Van Huis

2
Unicodeの比較規則に従って、大文字と小文字を変換して比較することは完全には正しくないことは間違いありません。これはいくつかのこと(つまり、構文解析コンテキストでのみ一般的に使用される大文字小文字の折り畳み)で機能しますが、自然言語では、大文字と小文字の両方で、等しく比較する必要がある2つの文字列が一致しない特殊なケースがあります。しかし、私はすぐに例を思いつくことはできません。
nielsm 2010年

7
動作しません。一部の奇妙な国際文字は、小文字/大文字に変換されると複数の文字に変換されます。例:"ß".toUpperCase().equals("SS")
Simon

ßはほとんど奇妙なキャラクターではなく、ドイツとオーストリアでのみ使用されている国際的なものでもありません。しかし、はい、これは得られるのと同じくらい良いですが、実際には大文字と小文字を区別しない比較ではありません。nielsmは3年前にすでに指摘しています。
ジョーイ

トルコ語のUnicodeでは機能しません。これは、誰かの電子メールから直接送信されます。
Alexander

43

indexOf(String)メソッドでは大文字と小文字が区別されますか?

はい、大文字と小文字が区別されます。

@Test
public void indexOfIsCaseSensitive() {
    assertTrue("Hello World!".indexOf("Hello") != -1);
    assertTrue("Hello World!".indexOf("hello") == -1);
}

もしそうなら、それの大文字と小文字を区別しないバージョンはありますか?

いいえ、ありません。indexOfを呼び出す前に、両方の文字列を小文字に変換できます。

@Test
public void caseInsensitiveIndexOf() {
    assertTrue("Hello World!".toLowerCase().indexOf("Hello".toLowerCase()) != -1);
    assertTrue("Hello World!".toLowerCase().indexOf("hello".toLowerCase()) != -1);
}

8
Locale.USでカルチャ不変変換を使用することを忘れないでください。トルコ語ロケールで実行されるJavaアプリケーションで十分な問題が発生しました。
idursun 2009

@ idursun-USロケールに強制しても問題は解決しません"ı".toLowerCase(Locale.US).indexOf("I".toLowerCase(Locale.US))。これは、最初から問題のある文字を実際に含む文字列では機能しないためです(たとえば、最初の文字列はトルコ語の小文字であるため、0を返す必要があります "I"。したがって"I"、2番目の大文字と同じように比較する必要がありますが、後者は"i"代わりに変換されるため、-1を返します)。
ジュール

20

Apache CommonsLangライブラリのStringUtilsクラスにケース無視メソッドがあります

indexOfIgnoreCase(CharSequence str、CharSequence searchStr)


現在の回答は、Unicode制御文字を含む特定の非ASCII文字列では機能しないため、これは受け入れられる回答であるはずです。たとえば、これはトルコ語で書かれたテキストに対して機能します。舞台裏では、ApacheはregionMatchesを使用しており、それは機能します。
Alexander Pogrebnyak 2014

17

はい、indexOf大文字と小文字が区別されます。

私が見つけたケースの無感覚を行うための最良の方法は次のとおりです。

String original;
int idx = original.toLowerCase().indexOf(someStr.toLowerCase());

大文字と小文字を区別しませんindexOf()


2
いいえ、絶対にしないでください。その理由は、original.toLowerCase().length()が常にに等しいとは限らないためですoriginal.length()。結果をにidx正しくマップすることはできませんoriginal
Cheok YanCheng19年

14

これがヒープメモリを割り当てない私のソリューションです。したがって、ここで説明した他のほとんどの実装よりも大幅に高速になるはずです。

public static int indexOfIgnoreCase(final String haystack,
                                    final String needle) {
    if (needle.isEmpty() || haystack.isEmpty()) {
        // Fallback to legacy behavior.
        return haystack.indexOf(needle);
    }

    for (int i = 0; i < haystack.length(); ++i) {
        // Early out, if possible.
        if (i + needle.length() > haystack.length()) {
            return -1;
        }

        // Attempt to match substring starting at position i of haystack.
        int j = 0;
        int ii = i;
        while (ii < haystack.length() && j < needle.length()) {
            char c = Character.toLowerCase(haystack.charAt(ii));
            char c2 = Character.toLowerCase(needle.charAt(j));
            if (c != c2) {
                break;
            }
            j++;
            ii++;
        }
        // Walked all the way to the end of the needle, return the start
        // position that this was found.
        if (j == needle.length()) {
            return i;
        }
    }

    return -1;
}

そして、これが正しい動作を検証する単体テストです。

@Test
public void testIndexOfIgnoreCase() {
    assertThat(StringUtils.indexOfIgnoreCase("A", "A"), is(0));
    assertThat(StringUtils.indexOfIgnoreCase("a", "A"), is(0));
    assertThat(StringUtils.indexOfIgnoreCase("A", "a"), is(0));
    assertThat(StringUtils.indexOfIgnoreCase("a", "a"), is(0));

    assertThat(StringUtils.indexOfIgnoreCase("a", "ba"), is(-1));
    assertThat(StringUtils.indexOfIgnoreCase("ba", "a"), is(1));

    assertThat(StringUtils.indexOfIgnoreCase("Royal Blue", " Royal Blue"), is(-1));
    assertThat(StringUtils.indexOfIgnoreCase(" Royal Blue", "Royal Blue"), is(1));
    assertThat(StringUtils.indexOfIgnoreCase("Royal Blue", "royal"), is(0));
    assertThat(StringUtils.indexOfIgnoreCase("Royal Blue", "oyal"), is(1));
    assertThat(StringUtils.indexOfIgnoreCase("Royal Blue", "al"), is(3));
    assertThat(StringUtils.indexOfIgnoreCase("", "royal"), is(-1));
    assertThat(StringUtils.indexOfIgnoreCase("Royal Blue", ""), is(0));
    assertThat(StringUtils.indexOfIgnoreCase("Royal Blue", "BLUE"), is(6));
    assertThat(StringUtils.indexOfIgnoreCase("Royal Blue", "BIGLONGSTRING"), is(-1));
    assertThat(StringUtils.indexOfIgnoreCase("Royal Blue", "Royal Blue LONGSTRING"), is(-1));  
}

これはどのように質問に答えますか?
高品質の触媒

7
答えは「いいえ、indexOfの大文字と小文字を区別しないバージョンはありません」です。ただし、解決策を探している人がこのページを見つけるため、ここに解決策を追加しました。私は自分のソリューションをテストケースで利用できるようにして、次の人が私のコードを使用してまったく同じ問題を解決できるようにしました。だからスタックオーバーフローは便利ですよね?私は高性能コードを書いた10年の経験があり、その半分はgoogleでです。コミュニティを支援するために、十分にテストされたソリューションを無料で提供しました。
Zach Vorhies 2015

3
これはまさに私が興味を持っていたものです。これはApacheCommonsバージョンよりも約10〜15%高速であることがわかりました。私がそれをもっと何度も賛成することができれば、私はそうするでしょう。ありがとう!
ジェフウィリアムズ

ジェフに感謝します、それがあなたにたくさんの価値を与えてくれてうれしいです。解決策を提供するこの投稿が上に行くことを推奨している他の人がいます。他の誰かが私のコードを気に入ったら、このソリューションに賛成してください。
Zach Vorhies 2015

2
ここでは不足しているテストケースがあります:assertThat(StringUtils.indexOfIgnoreCase("ı" /* Turkish lower-case I, U+0131 */, "I"), is(0));
ジュール・

10

はい、大文字と小文字が区別されます。indexOf検索する前にStringとStringパラメーターの両方を大文字に変換することで、大文字と小文字を区別しないようにすることができます。

String str = "Hello world";
String search = "hello";
str.toUpperCase().indexOf(search.toUpperCase());

toUpperCaseは状況によっては機能しない場合があることに注意してください。たとえばこれ:

String str = "Feldbergstraße 23, Mainz";
String find = "mainz";
int idxU = str.toUpperCase().indexOf (find.toUpperCase ());
int idxL = str.toLowerCase().indexOf (find.toLowerCase ());

idxUは20になりますが、これは間違っています。idxLは19になりますが、これは正しいです。問題の原因は、toUpperCase()が「ß」文字を2つの文字「SS」に変換し、これによりインデックスが破棄されることです。

したがって、常にtoLowerCase()を使用してください


1
小文字に固執しても役に立ちません。に変更findすると"STRASSE"、小文字のバリアントではまったく検出されませんが、大文字のバージョンでは正しく検出されます。
ジュール

3

返されたインデックス値をどのように処理していますか?

文字列を操作するためにそれを使用している場合、代わりに正規表現を使用できませんか?

import static org.junit.Assert.assertEquals;    
import org.junit.Test;

public class StringIndexOfRegexpTest {

    @Test
    public void testNastyIndexOfBasedReplace() {
        final String source = "Hello World";
        final int index = source.toLowerCase().indexOf("hello".toLowerCase());
        final String target = "Hi".concat(source.substring(index
                + "hello".length(), source.length()));
        assertEquals("Hi World", target);
    }

    @Test
    public void testSimpleRegexpBasedReplace() {
        final String source = "Hello World";
        final String target = source.replaceFirst("(?i)hello", "Hi");
        assertEquals("Hi World", target);
    }
}

ここでの賛成票の欠如に驚いた。不正解が多いページでは、これは実際に正しく機能する3つのうちの1つです。
ジュール


2
@Test
public void testIndexofCaseSensitive() {
    TestCase.assertEquals(-1, "abcDef".indexOf("d") );
}

これはさえ....テストに合格した場合にも言っていない、完全なquestion..itに答えていない
jjnguy

2
そうではありませんでした。元の質問者が自分でテストを実行し、習慣に入る可能性があることを期待していました
Paul McKenzie

2
まあ、それは問題ありません...しかし、私は、テストよりも実際に答えを与える質問に投票する方が良いと主張します。StackOverflowはコードQおよびAリポジトリになろうとしています。したがって、完全な答えが最善です。
jjnguy 2009

1
@jjnguy:私はいつも、テストを投稿した人が合格したテストを投稿したという印象を受けました。@dfaも同様のことをしました。(しかし、@ dfaの答えはより完全です)。
トム

しかし、彼はまた、いくつかの単語(説明)を投稿しました...それらは通常役に立ちます。
jjnguy 2009

2

はい、確かにそうです。標準ライブラリを使用してこれを回避する1つの方法は、次のとおりです。

int index = str.toUpperCase().indexOf("FOO"); 

2

同じ問題がありました。正規表現とapacheStringUtils.indexOfIgnoreCase-Methodを試しましたが、どちらもかなり遅いので、自分で短いメソッドを作成しました...:

public static int indexOfIgnoreCase(final String chkstr, final String searchStr, int i) {
    if (chkstr != null && searchStr != null && i > -1) {
          int serchStrLength = searchStr.length();
          char[] searchCharLc = new char[serchStrLength];
          char[] searchCharUc = new char[serchStrLength];
          searchStr.toUpperCase().getChars(0, serchStrLength, searchCharUc, 0);
          searchStr.toLowerCase().getChars(0, serchStrLength, searchCharLc, 0);
          int j = 0;
          for (int checkStrLength = chkstr.length(); i < checkStrLength; i++) {
                char charAt = chkstr.charAt(i);
                if (charAt == searchCharLc[j] || charAt == searchCharUc[j]) {
                     if (++j == serchStrLength) {
                           return i - j + 1;
                     }
                } else { // faster than: else if (j != 0) {
                         i = i - j;
                         j = 0;
                    }
              }
        }
        return -1;
  }

私のテストによると、はるかに高速です...(少なくともsearchStringがかなり短い場合)。改善やバグについて何か提案があれば、私に知らせてください...(私はこのコードをアプリケーションで使用しているので;-)


検索文字列は検索するテキストよりも大幅に短くなり、検索文字列の大文字と小文字のバージョンのみが作成されるため、これは実際には非常に巧妙です。有難うございます!
fiffy 2015年

私のテストでは、これはStringUtilsバージョンよりも大幅に低速です。ただし、ザックの答えは10〜15%速くなります。
ジェフウィリアムズ

このソリューションは、Zach Vorhiesが提供するソリューションよりも約10%高速です。この解決策をありがとう。
gogognome 2016年

このソリューションでは、大文字に変換すると長さが変わる文字列(たとえば、「ß」を検索すると、大文字の「S」が1つ含まれる文字列で検索されます)またはテキストが存在する場合、正解は生成されません。代替の大文字を使用します(たとえば、トルコ語のテキストの正しい大文字であるためindexOfIgnoreCase("İ","i")0を返す必要İがありますが、より一般的な大文字でiあるため-1を返します)。iI
ジュール

1

最初の質問はすでに何度も答えられています。はい、String.indexOf()メソッドはすべて大文字と小文字を区別します。

ロケールに依存indexOf()する必要がある場合は、Collat​​orを使用できます。設定した強度値に応じて、大文字と小文字を区別しない比較を行うことができます。また、アクセント付きの文字をアクセントなしの文字と同じように扱うこともできます。これを行う方法の例を次に示します。

private int indexOf(String original, String search) {
    Collator collator = Collator.getInstance();
    collator.setStrength(Collator.PRIMARY);
    for (int i = 0; i <= original.length() - search.length(); i++) {
        if (collator.equals(search, original.substring(i, i + search.length()))) {
            return i;
        }
    }
    return -1;
}

ここでの賛成票の欠如に驚いた。不正解が多いページでは、これは実際に正しく機能する3つのうちの1つです。
ジュール

1

要約すると、3つの解決策:

  • toLowerCase()またはtoUpperCaseを使用する
  • apacheのStringUtilsを使用する
  • 正規表現を使用する

さて、私が疑問に思っていたのは、どれが最速かということでしたか?私は平均して最初のものを推測しています。


0

しかし、それを書くのは難しいことではありません:

public class CaseInsensitiveIndexOfTest extends TestCase {
    public void testOne() throws Exception {
        assertEquals(2, caseInsensitiveIndexOf("ABC", "xxabcdef"));
    }

    public static int caseInsensitiveIndexOf(String substring, String string) {
        return string.toLowerCase().indexOf(substring.toLowerCase());
    }
}

上でコメントしたように、これ"ı"は、の小文字のバリアント(ほとんどの言語のデフォルトではない)であることを正しく識別できません"I"。または、デフォルトのロケールに設定されたマシンで実行した場合、"ı" "i"小文字のバリアントでもあることに気付かないでしょう"I"
ジュール

0

両方の文字列を小文字に変換することは通常大したことではありませんが、一部の文字列が長い場合は遅くなります。そして、これをループで行うと、それは本当に悪いことです。このため、をお勧めしindexOfIgnoreCaseます。


0
 static string Search(string factMessage, string b)
        {

            int index = factMessage.IndexOf(b, StringComparison.CurrentCultureIgnoreCase);
            string line = null;
            int i = index;
            if (i == -1)
            { return "not matched"; }
            else
            {
                while (factMessage[i] != ' ')
                {
                    line = line + factMessage[i];
                    i++;
                }

                return line;
            }

        }

1
それのようなこのルックスは、C#であるかもしれない
ウェストン

0

これは、ApacheのStringUtilsバージョンによく似たバージョンです。

public int indexOfIgnoreCase(String str, String searchStr) {
    return indexOfIgnoreCase(str, searchStr, 0);
}

public int indexOfIgnoreCase(String str, String searchStr, int fromIndex) {
    // /programming/14018478/string-contains-ignore-case/14018511
    if(str == null || searchStr == null) return -1;
    if (searchStr.length() == 0) return fromIndex;  // empty string found; use same behavior as Apache StringUtils
    final int endLimit = str.length() - searchStr.length() + 1;
    for (int i = fromIndex; i < endLimit; i++) {
        if (str.regionMatches(true, i, searchStr, 0, searchStr.length())) return i;
    }
    return -1;
}

0

私は、これまでに投稿された、実際に機能する唯一の解決策を主張したいと思います。:-)

対処しなければならない問題の3つのクラス。

  1. 小文字と大文字の非推移的なマッチングルール。トルコのI問題は、他の回答で頻繁に言及されています。String.regionMatchesのAndroidソースのコメントによると、大文字と小文字を区別しない同等性を比較する場合、グルジアの比較ルールでは小文字にさらに変換する必要があります。

  2. 大文字と小文字の文字数が異なる場合。これらの場合、これまでに投稿されたソリューションのほとんどすべてが失敗します。例:ドイツ語のSTRASSEとStraßeでは大文字と小文字が区別されませんが、長さが異なります。

  3. アクセントのある文字の結合力。ロケールとコンテキストは、アクセントが一致するかどうかに影響します。フランス語では、大文字のアクセントを使用する動きがありますが、「é」の大文字の形式は「E」です。カナダフランス語では、「é」の大文字は例外なく「É」です。両国のユーザーは、検索時に「e」が「é」と一致することを期待します。アクセント付き文字とアクセントなし文字が一致するかどうかは、ロケールによって異なります。ここで考えてみましょう:「E」は「É」と等しいですか?はい。します。とにかく、フランス語のロケールでは。

私は現在android.icu.text.StringSearch、大文字と小文字を区別しないindexOf操作の以前の実装を正しく実装するために使用しています。

Android以外のユーザーは、ICU4Jパッケージを使用して、 com.ibm.icu.text.StringSearchクラス。

AndroidとJREはどちらも他の名前空間(Collat​​orなど)に同じ名前のクラスがあるため、正しいicuパッケージ(android.icu.textまたはcom.ibm.icu.text)のクラスを参照するように注意してください。

    this.collator = (RuleBasedCollator)Collator.getInstance(locale);
    this.collator.setStrength(Collator.PRIMARY);

    ....

    StringSearch search = new StringSearch(
         pattern,
         new StringCharacterIterator(targetText),
         collator);
    int index = search.first();
    if (index != SearchString.DONE)
    {
        // remember that the match length may NOT equal the pattern length.
        length = search.getMatchLength();
        .... 
    }

テストケース(ロケール、パターン、ターゲットテキスト、expectedResult):

    testMatch(Locale.US,"AbCde","aBcDe",true);
    testMatch(Locale.US,"éèê","EEE",true);

    testMatch(Locale.GERMAN,"STRASSE","Straße",true);
    testMatch(Locale.FRENCH,"éèê","EEE",true);
    testMatch(Locale.FRENCH,"EEE","éèê",true);
    testMatch(Locale.FRENCH,"éèê","ÉÈÊ",true);

    testMatch(new Locale("tr-TR"),"TITLE","tıtle",true);  // Turkish dotless I/i
    testMatch(new Locale("tr-TR"),"TİTLE","title",true);  // Turkish dotted I/i
    testMatch(new Locale("tr-TR"),"TITLE","title",false);  // Dotless-I != dotted i.

PS:私が判断できる限りでは、ロケール固有のルールが辞書のルールに従ってアクセント付き文字とアクセントなし文字を区別する場合、PRIMARYバインディング強度は正しいことを行う必要があります。しかし、私はこの前提をテストするためにどのロケールを使用するかはわかりません。寄付されたテストケースをいただければ幸いです。


1
コードをデュアルライセンスする場合は、他のプラットフォームを介して行い、そこにリンクを含めてください。各回答の最後に追加された法律用語の大規模な塊は、StackOverflowに膨大な量の混乱を追加します。
meagar

次に、コードフラグメントに適用されるCC-BY-SAの問題に対処するためのより効率的な方法を見つける必要があります
Robin Davies

また、私が著作権を保持しているコードフラグメントに提供したライセンス付与を削除することも不適切と思われます。
ロビン

-2

indexOfでは大文字と小文字が区別されます。これは、equalsメソッドを使用してリスト内の要素を比較するためです。同じことがcontainsとremoveにも当てはまります。


元の質問は、StringのindexOfメソッドに関するものです。
John Topley

それが彼の話していることだとは知りませんでした。他の人が何かを言うまで、私はそれを知りませんでした。ただし、原則は同じです。
ロビー

2
いいえ、そうではありません。StringのindexOfメソッドの内部は、オブジェクトではなく文字を比較するため、equalsメソッドを使用しません。
John Topley
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.