当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何在Java中以不区分大小写的方式检查String是否包含另一个String?》经验,为你挑选了7个好方法。

说我有两个字符串,

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

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

return s1.contains(s2);

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

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

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



1> Dave L...:

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

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

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


这种方法使用`Pattern`比s1.toLowerCase()更高效.contains(s2.toLowerCase())`?
正如"Pattern.CASE_INSENSITIVE"的文档所述,这仅适用于ASCII字符(即"Ä"与"ä"不匹配).需要另外指定`UNICODE_CASE`标志来实现.
如果我们有更好的变量名,我会更清楚:`Pattern.compile(Pattern.quote(needle),Pattern.CASE_INSENSITIVE).matcher(haystack).find()`
@ user01我进行了速度分析.请参阅我的答案结果(我也展示了一个更快的解决方案):http://stackoverflow.com/a/25379180/1705598
@ user01正确性在性能之前出现,并且使用toLowerCase将给出可能不正确的结果(例如,在比较包含字母Sigma的某些希腊文本时,其具有相同大写形式的两个小写形式).

2> Matt Quail..:

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

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

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


在大多数情况下,.toLowerCase().contains()方法可能会更快.我可能更喜欢这种风格,以降低复杂性.
@AaronFerguson是的,确实,`toLowerCase().contains()`更快.我进行了一些速度分析,看看我的结果答案:http://stackoverflow.com/a/25379180/1705598
@MattQuail如果它可能不正确,它没有任何意义.例如,希腊大写sigma有两个小写形式(取决于它是否出现在一个单词的末尾),当尝试进行不区分大小写的子串匹配时,子串以sigma结尾,您很容易得到错误结果.

3> muhamadto..:

您可以使用

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

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


@CharlesWood它委托给`String.regionMatches`,它使用字符转换,所以没有.此外,`containsIgnoreCase("ß","ss")`返回-1,这在每个语言环境中都是错误的(德语"sharp s"大写为"ss".

4> icza..:

更快的实施:利用 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种方法.

    我们的containsIgnoreCase()方法.

    通过将两个字符串转换为小写并调用String.contains().

    通过将源字符串转换为小写字母并String.contains()使用预缓存的低级子字符串进行调用.这个解决方案已经不那么灵活,因为它测试了一个预先定义的子字符串.

    使用正则表达式(接受的答案Pattern.compile().matcher().find()......)

    使用正则表达式,但预先创建和缓存Pattern.此解决方案已经不那么灵活,因为它测试预定义的子字符串.

结果(通过调用方法1000万次):

    我们的方法:670毫秒

    2x toLowerCase()并包含():2829 ms

    1x toLowerCase()和contains(),缓存的子字符串:2446 ms

    Regexp:7180毫秒

    Regexp缓存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倍快比lowercasing和使用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");
    }

}


+1但请注意,它不能用于"ß"(德语尖锐的S;大写为"SS")以及其他一些字符(请参阅"String.regionMatches"的来源,它会尝试两次转换).
你总是测试相同的字符串,这不是一个公平的比较.'我'总是在中间,这可能会或可能不会对不同的搜索方法产生影响.更好的方法是生成随机字符串,并报告子字符串不存在时的速度.
这看起来非常接近Apache StringUtils方法:http://grepcode.com/file/repo1.maven.org/maven2/org.apache.commons/commons-lang3/3.1/org/apache/commons/lang3/StringUtils.java #StringUtils.containsIgnoreCase%28java.lang.CharSequence%2Cjava.lang.CharSequence 29%
@icza`CharSequenceUtils.regionMatches`实际上只调用`String.regionMatches`.无论如何,我的观点是提供信息,如果有人已经在使用StringUtils lib,他可以调用它,因为它似乎是一种有效的方式,就像你用你的基准证明它一样.如果我没有使用Apache lib,我肯定会使用你的方法;)

5> Phil..:

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

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


字符大小写取决于语言,这意味着它可以在您的计算机上运行但对客户来说会失败:).见@Adriaan Koster评论.

6> 小智..:

是的,这是可以实现的:

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


使用toLowerCase()的一个很大的缺点是结果取决于当前的Locale.请参阅:http://javapapers.com/core-java/javas-tolowercase-has-got-a-surprise-for-you/
这个问题实际上包含了一个更好的解决方案,因为这个问题对于非小写的`s2`是失败的.没有谈论这样的细节,例如这个没有编译,如果确实如此,它会返回一个字符串.
问题已包含此解决方案

7> 小智..:

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

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

推荐阅读
mobiledu2402851373
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有