indexOf区分大小写?


80

indexOf(String)方法区分大小写吗?如果是这样,是否有不区分大小写的版本?


3
并不是说我是一个出色的演奏家或其他任何人(我实际上认为性能调整是一种恶魔),但每次调用.toUpperCase都会复制您的字符串,因此如果您循环执行此操作,请尝试将.toUpperCase移出如果可能的话。
Bill K

Answers:


74

这些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比较规则,大小写转换然后进行比较并不完全正确。它适用于某些事物(即大小写折叠,通常只在语法分析上下文中使用),但对于自然语言,可能存在特殊情况,即在两个大写字母或两个小写字母下,应该比较的两个字符串不相等。但是,我无法提出任何示例。
nielsm,2010年

7
不行 当转换为小写/大写字母时,某些奇怪的国际字符会转换为多个字符。例如:"ß".toUpperCase().equals("SS")
Simon

ß几乎不是一个奇怪的字符,也几乎不是国际性的,仅在德国和奥地利使用。但是,是的,这和它取得的效果一样好,但实际上不像不区分大小写的比较那样,正如尼尔斯姆三年前已经指出的那样。
乔伊,

不适用于土耳其语unicode,这直接来自某人的电子邮件。
亚历山大·波格勒布纳克

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"第二个大写字母进行比较,但返回-1,因为后者被转换为大写字母"i")。
Jules '18

20

Apache Commons Lang库的StringUtils类中有一个case忽略方法

indexOfIgnoreCase(CharSequence str,CharSequence searchStr)


这应该是一个可以接受的答案,因为当前答案不适用于某些包含unicode控制字符的非ascii字符串。例如,这适用于以土耳其语编写的文本。在后台,Apache使用regionMatches,这确实起作用。
2014年

17

是的,indexOf区分大小写。

我发现区分大小写的最佳方法是:

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

这将不区分大小写indexOf()


2
不,永远不要那样做。原因是,original.toLowerCase().length()并不总是等于original.length()。结果idx无法正确映射回original
Cheok Yan Cheng

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版本”。但是,我在这里添加了解决方案,因为人们会在此页面中寻找解决方案。我在测试用例中提供了解决方案,以便下一个遇到的人可以使用我的代码来解决完全相同的问题。这就是为什么堆栈溢出有用吗?我有十年编写高性能代码的经验,其中一半在Google。我只是免费提供了经过测试的解决方案,以帮助社区。
Zach Vorhies 2015年

3
这正是我感兴趣的。我发现它比Apache Commons版本快10-15%。如果我可以投票更多次,我会。谢谢!
杰夫·威廉姆斯

谢谢杰夫,我很高兴为您带来了很多价值。还有其他一些人建议此提供解决方案的帖子转到顶部。如果其他人喜欢我的代码,那么我谦虚地请您支持此解决方案。
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 '18

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 '18


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

这甚至没有回答完整的问题。.它甚至没有说测试是否通过……
。– jjnguy

2
您说得对,我不是,我只是希望它会促使原始提问者对他/她自己进行测试,并养成习惯
Paul McKenzie

2
很好,但是...但是我想说,投票给一个实际上可以给出答案的问题比测试更好。StackOverflow试图成为代码Q和A的存储库。因此,完整的答案将是最好的。
jjnguy

1
@jjnguy:我总是对发布测试,发布通过测试的人印象深刻。@dfa做了类似的事情。(但是@dfa的答案更完整)。
汤姆(Tom)2009年

但他也张贴了一些文字(说明)...通常对您有所帮助。
jjnguy

2

是的,我相当确定。使用标准库解决此问题的一种方法是:

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很短的情况下)。如果您有任何改进或错误的建议,不妨通知我...(因为我在应用程序中使用了此代码;-)


这实际上非常聪明,因为搜索字符串会比要搜索的文本短得多,并且只会创建搜索字符串的大小写形式。谢谢你!
2015年

在我的测试中,这比StringUtils版本要慢得多。但是,扎克(Zach)的回答快了10-15%。
杰夫·威廉姆斯

此解决方案比Zach Vorhies提供的解决方案快约10%。谢谢您的解决方案。
gogognome

当字符串转换为大写时会改变长度(例如,如果您搜索“ß”,它将在任何包含单个大写字母“ S”的字符串中找到该字符串),则该解决方案无法产生正确的答案使用替代的大写字母(例如,indexOfIgnoreCase("İ","i")应返回0,因为它İi土耳其语文本的正确大写字母,但应返回-1,因为它i是大写字母I)。
Jules '18

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

感到惊讶的是这里缺乏支持。在以错误答案为主的页面中,这是实际上正确运行的仅有的三个之一。
Jules '18

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"
Jules '18

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问题。根据Android源中String.regionMatches的注释,格鲁吉亚比较规则要求在不区分大小写的相等进行比较时将其转换为小写。

  2. 大写和小写形式的字母数量不同的情况。在这些情况下,到目前为止发布的所有解决方案几乎都失败了。示例:德语STRASSE与Straße具有不区分大小写的相等性,但长度不同。

  3. 重音字符的绑定强度。区域设置和上下文会影响重音是否匹配。在法语中,“é”的大写形式为“ E”,尽管有使用大写重音的趋势。在加拿大法语中,“é”的大写形式毫无例外地是“É”。两个国家/地区的用户在搜索时都希望“ e”与“é”匹配。重音字符和不重音字符是否匹配是特定于语言环境的。现在考虑:“ E”等于“É”吗?是。是的 无论如何,在法国语言环境中。

我目前正在android.icu.text.StringSearch用来正确实现不区分大小写的indexOf操作的先前实现。

非Android用户可以使用com.ibm.icu.text.StringSearch该类通过ICU4J包访问相同的功能。

请注意,在正确的icu包(android.icu.textcom.ibm.icu.text)中引用类,因为Android和JRE在其他名称空间(例如Collat​​or)中都具有相同名称的类。

    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绑定强度应该做正确的事;但我不使用哪种语言环境来测试此前提。捐赠的测试用例将不胜感激。

-

版权声明:由于应用于代码片段的StackOverflow的CC-BY_SA版权对于专业开发人员而言是行不通的,因此这些片段在以下更适当的许可下具有双重许可:https : //pastebin.com/1YhFWmnU


如果您想双重授权您的代码,请通过其他平台进行,并在其中添加链接。在每个答案的末尾附加了大量的法文,这给堆栈溢出带来了极大的混乱。
meagar

然后,也许您应该找到一种更有效的方法来解决CC-BY-SA应用于代码片段的问题,
Robin Davies

对于您来说,删除我提供给我拥有版权的代码片段的许可授权似乎也不合适。
罗宾·戴维斯

-2

indexOf区分大小写。这是因为它使用equals方法比较列表中的元素。包含和删除也一样。


最初的问题是关于String的indexOf方法的。
约翰·托普利2009年

我不知道他在说什么。直到其他人说了几句话,我才意识到。虽然原理仍然相同。
罗比

2
不,不是。String的indexOf方法的内部函数比较字符而不是对象,因此它不使用equals方法。
约翰·托普利
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.