indexOf 대소 문자를 구분합니까?


81

indexOf (String) 메서드는 대소 문자를 구분합니까? 그렇다면 대소 문자를 구분하지 않는 버전이 있습니까?


3
내가 큰 성능을 가진 사람이 아니거나 (실제로 성능 튜닝이 악하다고 생각합니다) .toUpperCase는 호출 할 때마다 문자열을 복사하므로 루프 에서이 작업을 수행하면 .toUpperCase를 밖으로 이동하십시오. 가능한 경우 루프의.
Bill 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
대 / 소문자 변환 및 비교가 유니 코드 비교 규칙에 따라 완전히 정확하지 않다고 확신합니다. 어떤 경우에는 작동하지만 (일반적으로 구문 구문 분석 컨텍스트에서만 사용되는 대소 문자 접기) 자연어의 경우 대문자 또는 소문자 모두에서 동일하게 비교해야하는 두 문자열이 일치하지 않는 특수한 경우가있을 수 있습니다. 그러나 나는 방망이에서 어떤 예도 생각 해낼 수 없습니다.
nielsm 2010 년

7
작동하지 않습니다. 일부 이상한 국제 문자는 소문자 / 대문자로 변환 할 때 여러 문자로 변환됩니다. 예 :"ß".toUpperCase().equals("SS")
Simon

ß는 이상한 문자가 아니며 독일과 오스트리아에서만 사용되는 국제적인 것도 거의 없습니다. 그러나 예, 이것은 3 년 전에 nielsm이 이미 지적했듯이 실제로 대소 문자를 구분하지 않는 비교는 아닙니다.
Joey

누군가의 이메일에서 직접 가져온 터키어 유니 코드에서는 작동하지 않습니다.
Alexander Pogrebnyak

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

@idursun을 미국 로케일로 강제 적용해도 문제가 해결되지 않습니다. 실제로 시작하는 데 문제가있는 문자를 포함하는 문자열에 대해서는 여전히 작동하지 않기 때문입니다 (예를 들어 "ı".toLowerCase(Locale.US).indexOf("I".toLowerCase(Locale.US))첫 번째 문자열이 터키어 소문자이기 때문에 0을 반환해야합니다 "I". 따라서 "I"두 번째 대문자와 동일하게 비교해야 하지만 후자가 "i"대신 변환되므로 -1을 반환합니다 .
Jules

20

Apache Commons Lang 라이브러리의 StringUtils 클래스에는 대소 문자 무시 메소드가 있습니다.

indexOfIgnoreCase (CharSequence str, CharSequence searchStr)


현재 답변은 유니 코드 제어 문자를 포함하는 특정 비 ASCII 문자열에 대해 작동하지 않기 때문에 허용되는 답변이어야합니다. 예를 들어, 이것은 터키어로 작성된 텍스트에서 작동합니다. 배후에서 Apache는 regionMatches를 사용하며 작동합니다.
Alexander Pogrebnyak

17

예, indexOf대소 문자를 구분합니다.

내가 찾은 대소 문자 무감각을 수행하는 가장 좋은 방법은 다음과 같습니다.

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

대소 문자를 구분하지 않습니다 indexOf().


2
아뇨. 절대 그렇게하지 마세요. 그 이유는 original.toLowerCase().length()항상 original.length(). 결과 idx는에 올바르게 다시 매핑 할 수 없습니다 original.
Cheok Yan Cheng 19-01-15

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

이것은 질문에 어떻게 대답합니까 ??
Quality Catalyst

7
대답은 "아니요, indexOf의 대소 문자를 구분하지 않는 버전이 없습니다"입니다. 그러나 사람들이 솔루션을 찾는이 페이지를 찾을 것이기 때문에 여기에 솔루션을 추가했습니다. 다음 사람이 내 코드를 사용하여 똑같은 문제를 해결할 수 있도록 테스트 케이스와 함께 솔루션을 제공했습니다. 그래서 스택 오버플로가 유용할까요? 저는 10 년 동안 고성능 코드를 작성한 경험이 있습니다. 그 중 절반은 Google입니다. 커뮤니티를 돕기 위해 잘 테스트 된 솔루션을 무료로 제공했습니다.
Zach Vorhies 2015

3
이것이 바로 제가 관심을 갖고 있었던 것입니다. Apache Commons 버전보다 약 10-15 % 더 빠른 것으로 나타났습니다. 더 많이 찬성 할 수 있다면 그렇게 할 것입니다. 감사!
Jeff Williams

Jeff에게 감사합니다. 많은 가치를 제공하게되어 기쁩니다. 해결책을 제공하는이 게시물이 정상을 향하도록 권장하는 다른 사람들이 있습니다. 다른 사람이 내 코드를 좋아한다면이 솔루션을 찬성 해달라고 겸손히 부탁드립니다.
Zach Vorhies 2015

2
다음은 누락 된 테스트 케이스입니다.assertThat(StringUtils.indexOfIgnoreCase("ı" /* Turkish lower-case I, U+0131 */, "I"), is(0));
Jules

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 ()가 "ß"문자를 "SS"라는 두 문자로 변환하고 이로 인해 색인이 해제된다는 것입니다.

따라서 항상 toLowerCase ()를 고수하십시오.


1
변경할 경우 : 소문자로 대항하는 것은 도움이되지 않습니다 find위해 "STRASSE", 그것은 낮은 경우 변형의 모든에서 그것을 발견하지 않지만, 제대로 대문자 버전에서 찾을 수 없습니다.
Jules

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

여기에 찬성표가 없다는 사실에 놀랐습니다. 오답이 많은 페이지에서 이것은 실제로 올바르게 작동하는 유일한 세 가지 중 하나입니다.
Jules

2

방금 출처를 살펴 보았습니다. 문자를 비교하므로 대소 문자를 구분합니다.


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

이도 .... 테스트를 통과하는 경우도 말하지 않는다 전체 question..it에 응답하지 않습니다
jjnguy

2
당신이 바로 내가, 내가 좀이 테스트 그를 / 자신을 실행하는 원래 질문자 메시지를 표시하고, 어쩌면 습관을 것이라고 기대했다하지 않은 것
폴 맥켄지

2
음, 괜찮습니다 ...하지만 시험보다 실제로 답을주는 질문에 투표하는 것이 낫다고 주장합니다. StackOverflow는 코드 Q 및 A 저장소가 되려고합니다. 따라서 완전한 답변이 가장 좋습니다.
jjnguy

1
@jjnguy : 저는 항상 테스트를 게시하고 통과 한 테스트를 게시 한 사람들의 인상을 받았습니다. @dfa는 비슷한 일을했습니다. (그러나 @dfa의 대답은 더 완벽합니다).

그러나 그는 또한 몇 가지 단어 (설명)를 게시했습니다 ... 그것들은 보통 도움이됩니다.
jjnguy

2

예, 확실합니다. 표준 라이브러리를 사용하여이를 해결하는 한 가지 방법은 다음과 같습니다.

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

2

같은 문제가있었습니다. 정규식과 아파치 StringUtils.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

이것은 내 테스트에서 StringUtils 버전보다 훨씬 느립니다. 그러나 Zach의 대답은 10-15 % 더 빠릅니다.
Jeff Williams

이 솔루션은 Zach Vorhies가 제공 한 솔루션보다 약 10 % 빠릅니다. 이 솔루션에 감사드립니다.
gogognome

이 솔루션은 대문자로 변환 할 때 길이를 변경하는 문자열 (예 : "ß"를 검색하면 대문자 "S"하나를 포함하는 모든 문자열에서 검색 됨) 또는 다음과 같은 텍스트가있는 경우 정답을 생성하지 않습니다. 대체 대문자를 사용합니다 (예 : indexOfIgnoreCase("İ","i")는 터키어 텍스트 İ의 올바른 대문자 이므로 0을 반환해야 i하지만 대신 i에 더 일반적인 대문자로 표시 되므로 -1을 반환합니다 I).
Jules

1

첫 번째 질문은 이미 여러 번 답변되었습니다. 예, String.indexOf()방법은 모두 대소 문자를 구분합니다.

로케일을 구분해야하는 경우 Collator를indexOf() 사용할 수 있습니다 . 설정 한 강도 값에 따라 대 / 소문자를 구분하지 않는 비교를 얻을 수 있으며, 악센트가있는 문자를 악센트가없는 문자와 동일하게 처리 할 수 ​​있습니다. 다음은이를 수행하는 방법의 예입니다.

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

여기에 찬성표가 없다는 사실에 놀랐습니다. 오답이 많은 페이지에서 이것은 실제로 올바르게 작동하는 유일한 세 가지 중 하나입니다.
Jules

1

요약하자면 3 가지 솔루션 :

  • toLowerCase () 또는 toUpperCase 사용
  • 아파치의 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".
Jules

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 #처럼 보입니다
weston

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

나는 지금까지 실제로 작동하는 유일한 해결책을 게시하고 싶다고 주장하고 싶습니다. :-)

처리해야 할 문제의 세 가지 부류.

  1. 소문자 및 대문자에 대한 비전 이적 일치 규칙. 터키 I 문제는 다른 답변에서 자주 언급되었습니다. String.regionMatches에 대한 Android 소스의 주석에 따르면 그루지야 어 비교 규칙은 대소 문자를 구분하지 않는 동등성을 비교할 때 소문자로 추가 변환해야합니다.

  2. 대문자와 소문자 형식의 문자 수가 다른 경우. 지금까지 게시 된 거의 모든 솔루션이 이러한 경우에 실패합니다. 예 : 독일어 STRASSE 대 Straße는 대소 문자를 구분하지 않지만 길이가 다릅니다.

  3. 악센트 부호가있는 문자의 바인딩 강도. 액센트 일치 여부에 관계없이 로케일 및 컨텍스트 효과. 프랑스어에서 'é'의 대문자 형태는 'E'이지만 대문자 악센트를 사용하는 움직임이 있습니다. 캐나다 프랑스어에서 'é'의 대문자 형식은 예외없이 'É'입니다. 두 국가의 사용자는 검색 할 때 "e"가 "é"와 일치 할 것으로 예상합니다. 악센트가있는 문자와 악센트가없는 문자가 일치하는지 여부는 로케일에 따라 다릅니다. 이제 "E"가 "É"와 같습니까? 예. 그렇습니다. 어쨌든 프랑스어 로케일에서는.

현재 android.icu.text.StringSearch대소 문자를 구분하지 않는 indexOf 작업의 이전 구현을 올바르게 구현 하는 데 사용 하고 있습니다.

Android가 아닌 사용자는 com.ibm.icu.text.StringSearch클래스를 사용하여 ICU4J 패키지를 통해 동일한 기능에 액세스 할 수 있습니다 .

Android와 JRE 모두 다른 네임 스페이스 (예 : Collator)에 동일한 이름의 클래스가 있으므로 올바른 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.

추신 : 내가 결정할 수있는 한, PRIMARY 바인딩 강도는 로케일 특정 규칙이 사전 규칙에 따라 악센트 부호가있는 문자와 비 강세 부호가없는 문자를 구분할 때 올바른 일을해야합니다. 하지만이 전제를 테스트하는 데 사용할 로케일은 없습니다. 기증 된 테스트 케이스는 감사하게 생각합니다.


1
코드를 이중으로 라이센스하려면 다른 플랫폼을 통해 수행하고 링크를 포함하십시오. 각 답변 끝에 엄청난 양의 법률 용어가 추가되면 Stack Overflow에 엄청난 양의 혼란이 추가됩니다.
메가

아마도 당신의 문제를 해결하기 위해보다 효율적인 방법 찾아야한다 CC를-BY-SA는, 코드 부분에 적용
로빈 데이비스에게

또한 내가 저작권을 보유한 코드 조각에 제공 한 라이센스 부여를 제거하는 것도 부적절 해 보입니다.
Robin Davies

-2

indexOf는 대소 문자를 구분합니다. 목록의 요소를 비교하기 위해 equals 메소드를 사용하기 때문입니다. 포함 및 제거도 마찬가지입니다.


원래 질문은 String의 indexOf 메소드에 관한 것입니다.
John Topley

그가 그게 무슨 말인지 몰랐습니다. 다른 사람들이 말을 할 때까지 나는 그것을 깨닫지 못했습니다. 원칙은 여전히 ​​동일합니다.
Robbie

2
아니에요. String의 indexOf 메소드 내부는 객체가 아닌 문자를 비교하므로 equals 메소드를 사용하지 않습니다.
John Topley
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.