it-swarm.cn

如何在Java的中以不区分大小写的方式检查字符串是否包含另一个字符串?

说我有两个字符串,

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

我想执行一个检查,返回s2包含在s1中。我可以这样做:

return s1.contains(s2);

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

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

除此之外,还有另一种(可能更好的)方法来实现这一目标而不关心区分大小写吗?

347
Aaron

是的,包含区分大小写。您可以将Java.util.regex.Pattern与CASE_INSENSITIVE标志一起用于不区分大小写的匹配:

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

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

300
Dave L.

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

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

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

您可以使用

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

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

132
muhamadto

更快的实现:利用String.regionMatches()

使用regexp可能会相对较慢。如果您只是想检查一个案例,那么(缓慢)并不重要。但是如果你有一个数组或数千或数十万个字符串的集合,那么事情就会变得非常缓慢。

下面给出的解决方案不使用正则表达式也不使用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()并包含():2829 ms
  3. 1x toLowerCase()和contains(),缓存的子字符串:2446 ms
  4. Regexp: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倍与使用正则表达式相比,快3倍即使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");
    }

}
105
icza

这样做的一种更简单的方法(不用担心模式匹配)会将Strings转换为小写:

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

是的,这是可以实现的:

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

16
Bilbo Baggins

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

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

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

  1. 将全部转换为小写

    for (SongInformation song: songs) {
        if (song.artist.toLowerCase().indexOf(pattern.toLowercase() > -1) {
                ...
        }
    }
    
  2. 使用String 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毫秒

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

3
Jan Newmarch

如果你拉入ICU4j,这里有一些你可以使用的非常友好的。我猜“忽略大小写”对于方法名称是有问题的,因为虽然主要强度比较确实忽略了大小写,但它被描述为特定于区域设置的。但它有望以一种用户期望的方式依赖于语言环境。

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
Trejkaz

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

1
SCdF
"AbCd".toLowerCase().contains("abcD".toLowerCase())
1
Takhir Atamuratov

我们可以使用带有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
Soudipta Dutta
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

0
seth

如果你必须在另一个ASCII字符串中搜索ASCII字符串,例如 _ url _ ,你会发现我的解决方案更好。我已经测试了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
Revertron

有一个简单的简洁方法,使用正则表达式标志(不区分大小写{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
Mr.Q
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");
        }

    }

}
0
sgrillon