Stri"/>

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

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

How to check if a String contains another String in a case insensitive manner in Java?

说我有两个字符串,

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

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

1
return s1.contains(s2);

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

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

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


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

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

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


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

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

1
Pattern.compile(Pattern.quote(s2), Pattern.CASE_INSENSITIVE).matcher(s1).find();


您可以使用

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

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


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

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

下面介绍的解决方案不使用正则表达式也不使用toLowerCase()(这也很慢,因为它会创建另一个字符串,并在检查后将它们抛出)。

该解决方案基于String.regionMatches()方法构建,该方法似乎未知。它检查2 String个区域是否匹配,但重要的是它还有一个带有方便的ignoreCase参数的重载。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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种方法。

  • 我们的containsIgnoreCase()方法。
  • 通过将两个字符串转换为小写并调用String.contains()
  • 通过将源字符串转换为小写字母并使用预缓存的低级子字符串调用String.contains()。这个解决方案已经不那么灵活,因为它测试了一个预先定义的子字符串。
  • 使用正则表达式(接受的答案Pattern.compile().matcher().find() ...)
  • 使用正则表达式,但使用预先创建和缓存的Pattern。此解决方案已经不那么灵活,因为它测试预定义的子字符串。
  • 结果(通过调用方法1000万次):

  • 我们的方法:670毫秒
  • 2x toLowerCase()并包含():2829 ms
  • 1x toLowerCase()和contains(),缓存的子字符串:2446 ms
  • Regexp:7180毫秒
  • 带有缓存Pattern的正则表达式:1845毫秒
  • 结果表:

    1
    2
    3
    4
    5
    6
    7
    8
                                                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

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

    分析测试代码

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    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");
        }

    }

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

    1
    2
    3
    4
    5
    String foobar ="fooBar";
    String bar ="FOO";
    if (foobar.toLowerCase().contains(bar.toLowerCase()) {
        System.out.println("It's a match!");
    }

    是的,这是可以实现的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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!"因为它发现你的角色被包含了。


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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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();
    }

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

  • 将全部转换为小写

    1
    2
    3
    4
    5
    for (SongInformation song: songs) {
        if (song.artist.toLowerCase().indexOf(pattern.toLowercase() > -1) {
                ...
        }
    }
  • 使用String matches()方法

    1
    2
    3
    4
    5
    for (SongInformation song: songs) {
        if (song.artist.matches("(?i).*" + pattern +".*")) {
        ...
        }
    }
  • 使用正则表达式

    1
    2
    3
    4
    5
    6
    7
    8
    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毫秒

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


    1
    "AbCd".toLowerCase().contains("abcD".toLowerCase())

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


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


    如果您必须在另一个ASCII字符串(例如URL)中搜索ASCII字符串,您会发现我的解决方案更好。我已经测试了icza的方法和我的速度,以下是结果:

    • 案例1耗时2788毫秒 - regionMatches
    • 案例2耗时1520毫秒 - 我的

    代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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));
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    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");
            }

        }

    }

    我们可以使用带有anyMatch的流和包含Java 8的流

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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;
        }

    }

    有一个简单的简洁方法,使用正则表达式标志(不区分大小写{i}):

    1
    2
    3
    4
    5
    6
    7
    8
     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)
     * */


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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


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

    1
    2
    3
    4
    String s1 ="AbBaCca";
    String s2 ="bac";
    String toLower = s1.toLowerCase();
    return toLower.contains(s2);

    1
    2
    String x="abCd";
    System.out.println(Pattern.compile("c",Pattern.CASE_INSENSITIVE).matcher(x).find());

    推荐阅读

      linux检查硬盘的命令?

      linux检查硬盘的命令?,系统,信息,检测,情况,命令,工具,电脑,地址,设备,硬盘,l

      字符串查找命令linux?

      字符串查找命令linux?,系统,字符串,工具,信息,文件,命令,字符,选项,文本,范

      linux命令替换字符串?

      linux命令替换字符串?,字符串,文件,批量,首次,数据,命令,内容,方法,用字,结

      linux检查挂载命令?

      linux检查挂载命令?,设备,系统,信息,情况,状态,服务,软件,命令,磁盘,网络,lin

      linux拼接字符串命令?

      linux拼接字符串命令?,系统,工作,代码,工具,名称,信息,地址,时间,数据,命令,l

      添加字符串命令linux?

      添加字符串命令linux?,情况,名称,文件,位置,名字,地方,连续,信息,命令,内容,L

      linux一般检查命令?

      linux一般检查命令?,网络,系统,检测,情况,工作,信息,命令,进程,时间,设备,lin

      检查硬件linux命令?

      检查硬件linux命令?,信息,系统,第一,数据,设备,检测,命令,情况,灵活,实时,如

      检查路由命令linux?

      检查路由命令linux?,网络,地址,系统,信息,工具,电脑,时间,通信,服务,命令,lin

      linux命令查找字符串?

      linux命令查找字符串?,工具,信息,命令,字符串,系统,工作,文件,范本,样式,文

      linux数据库检查命令?

      linux数据库检查命令?,服务,状态,地址,位置,系统,信息,命令,工作,情况,密码,

      linux分区检查命令是?

      linux分区检查命令是?,系统,设备,工具,管理,情况,信息,检测,分区,密码,单位,

      linux检查流量的命令?

      linux检查流量的命令?,工具,系统,实时,状态,网络,信息,数据,密码,地址,流量,l

      linux检查更新命令是?

      linux检查更新命令是?,工作,软件,地址,系统,信息,管理,命令,目录,最新,标准,l

      命令检查linux版本?

      命令检查linux版本?,系统,地址,信息,发行,名称,电脑,版本,连续,工具,周期,在L

      linux内存检查命令?

      linux内存检查命令?,情况,系统,信息,工具,实时,分析,状态,内存,命令,总量,查

      linux下节点检查命令?

      linux下节点检查命令?,系统,信息,工作,名称,命令,地址,情况,文件,服务,第一,l

      linux命令字符串匹配?

      linux命令字符串匹配?,系统,工具,命令,字符串,灵活,状态,文件,文本,模式,管

      python字符串截取?

      python字符串截取?,代码,步长,位置,分析,字符串,字符,信息,灵活,数字,表示,在