441

假设我有两个字符串,

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

我想执行s2包含在s1. 我可以这样做:

return s1.contains(s2);

我很确定这contains()是区分大小写的,但是我无法通过阅读文档确定这一点。如果是这样,我想我最好的方法是:

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

除了所有这些,还有另一种(可能更好)的方法来实现这一点而不关心区分大小写吗?

4

20 回答 20

347

是的,包含区分大小写。您可以使用带有 CASE_INSENSITIVE 标志的 java.util.regex.Pattern 进行不区分大小写的匹配:

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

编辑:如果 s2 包含正则表达式特殊字符(其中有很多),首先引用它很重要。我已经更正了我的答案,因为这是人们会看到的第一个答案,但是自从马特·奎尔指出这一点后,就投了赞成票。

于 2008-09-17T19:41:55.583 回答
283

Dave L. 回答的一个问题是当 s2 包含正则表达式标记时,例如\d, 等。

您想在 s2 上调用 Pattern.quote():

Pattern.compile(Pattern.quote(s2), Pattern.CASE_INSENSITIVE).matcher(s1).find();
于 2008-09-18T07:48:30.127 回答
196

您可以使用

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

Apache Commons库对于这类事情非常有用。而且这个特定的表达式可能比正则表达式更好,因为正则表达式在性能方面总是很昂贵。

于 2012-03-05T00:36:51.183 回答
137

更快的实现:利用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 毫秒
  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

与小写和 using 相比,我们的方法快 4 倍与使用正则表达式相比快 10倍,即使预缓存(并且失去检查任意子字符串的灵活性)也快 3 倍。contains()Pattern


分析测试代码

如果您对分析的执行方式感兴趣,这里是完整的可运行应用程序:

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

}
于 2014-08-19T08:48:16.603 回答
35

一种更简单的方法(不用担心模式匹配)是将两个Strings 都转换为小写:

String foobar = "fooBar";
String bar = "FOO";
if (foobar.toLowerCase().contains(bar.toLowerCase()) {
    System.out.println("It's a match!");
}
于 2012-01-16T17:29:04.673 回答
19

是的,这是可以实现的:

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!” 因为它发现你的角色被包含在内。

于 2010-04-15T16:30:39.200 回答
7

您可以使用正则表达式,它可以工作:

boolean found = s1.matches("(?i).*" + s2+ ".*");
于 2012-10-20T01:53:54.827 回答
3

我做了一个测试,找到一个不区分大小写的字符串匹配。我有一个包含 150,000 个对象的向量,所有对象都以字符串作为一个字段,并希望找到与字符串匹配的子集。我尝试了三种方法:

  1. 全部转换为小写

    for (SongInformation song: songs) {
        if (song.artist.toLowerCase().indexOf(pattern.toLowercase() > -1) {
                ...
        }
    }
    
  2. 使用字符串匹配()方法

    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 毫秒

对于这个用例,正则表达式看起来是最快的。

于 2014-01-16T05:21:27.177 回答
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();
}
于 2014-05-27T14:29:18.177 回答
3

有一个简单简洁的方法,使用正则表达式标志(不区分大小写 {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)
 * */
于 2019-05-04T06:41:13.180 回答
2
"AbCd".toLowerCase().contains("abcD".toLowerCase())
于 2017-12-13T07:36:31.167 回答
2

一种方法是使用 toLowerCase() 或 toUpperCase() 方法将两个字符串转换为小写或大写并进行测试。

public class Sample {
   public static void main(String args[]){
      String str = "Hello Welcome to insensitive Container";
      String test = "Java Testing";
      Boolean bool = str.toLowerCase().contains(test.toLowerCase());
      System.out.println(bool);
   }
}

这是使用带有 CASE_INSENSITIVE 标志的 java.util.regex.Pattern 进行不区分大小写匹配的另一种方法。

Pattern.compile(Pattern.quote(s2), Pattern.CASE_INSENSITIVE).matcher(s1).find();
于 2021-05-27T06:01:58.780 回答
1

我不确定您的主要问题是什么,但是是的,.contains 区分大小写。

于 2008-09-17T19:42:16.550 回答
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;
}

基本上,它是一种需要两个字符串的方法。它应该是不区分大小写的 contains() 版本。使用 contains 方法时,您想查看一个字符串是否包含在另一个字符串中。

此方法采用“sub”字符串并检查它是否等于容器字符串的长度等于“sub”的子字符串。如果您查看for循环,您将看到它在容器字符串上的子字符串(即“sub”的长度)中迭代。

每次迭代都会检查容器字符串equalsIgnoreCase的子字符串是否是子字符串。

于 2014-02-08T00:36:23.747 回答
0

如果您必须在另一个 ASCII 字符串(例如URL )中搜索 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));
}
于 2015-04-07T07:16:22.753 回答
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");
        }

    }

}
于 2016-11-09T13:24:46.433 回答
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;
    }

}
于 2019-05-03T09:48:10.963 回答
0

或者您可以使用一种简单的方法,只需将字符串的大小写转换为子字符串的大小写,然后使用 contains 方法。

于 2019-08-01T21:10:17.873 回答
-1
String x="abCd";
System.out.println(Pattern.compile("c",Pattern.CASE_INSENSITIVE).matcher(x).find());
于 2010-12-09T11:24:13.947 回答
-1

你可以简单地做这样的事情:

String s1 = "AbBaCca";
String s2 = "bac";
String toLower = s1.toLowerCase();
return toLower.contains(s2);
于 2015-12-03T15:16:30.133 回答