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

时间:2008-09-17 19:37:33

标签: java string

说我有两个字符串,

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

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

return s1.contains(s2);

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

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

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

20 个答案:

答案 0 :(得分:300)

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

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

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

答案 1 :(得分:246)

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

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

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

答案 2 :(得分:133)

您可以使用

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

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

答案 3 :(得分:106)

更快的实施:利用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。此解决方案已经不那么灵活,因为它会测试预定义的子字符串。
  6. 结果(通过调用方法1000万次):

    1. 我们的方法:670毫秒
    2. 2x toLowerCase()并包含():2829 ms
    3. 1x toLowerCase()和contains()with cached substring:2446 ms
    4. Regexp:7180 ms
    5. Regexp with cached Pattern:1845 ms
    6. 表中的结果:

                                                  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()快10倍时,我们的方法快4倍 快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");
          }
      
      }
      

答案 4 :(得分:18)

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

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

答案 5 :(得分:16)

是的,这是可以实现的:

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

答案 6 :(得分:6)

您可以使用regular expressions,它可以运作:

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

答案 7 :(得分:3)

我做了一个测试,找到一个字符串不区分大小写的匹配项。我有一个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()) {
        ...
        }
    }
    
  4. 时间结果是:

    • 未尝试匹配:20 msecs

    • 降低匹配:182 msecs

    • 字符串匹配:278 msecs

    • 正则表达式:65 msecs

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

答案 8 :(得分:3)

如果您使用ICU4j,可以使用一些非常友好的Unicode。我想&#34;忽略案例&#34;对于方法名称是有问题的,因为虽然主要强度比较确实忽略了大小写,但它被描述为与区域设置相关的细节。但它有望以某种方式依赖于语言环境。

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

答案 9 :(得分:1)

使用 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();

答案 10 :(得分:1)

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

答案 11 :(得分:1)

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

答案 12 :(得分: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

答案 13 :(得分:0)

如果您必须在另一个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));
}

答案 14 :(得分:0)

:://Synchro.bat
:://SachaDee 2014
@echo off&cls
:: We set Folders to synchonized
set "Folders= C:\HackooTest E:\Backup\Folder1 E:\Backup\Folder2 E:\Backup\Folder3"

for %%a in (%Folders%) do (
  for %%b in (%Folders%) do (
     if not "%%a"=="%%b" (
        set "VAR%%a%%b=%%a %%b"
        )
    )
)
for /f "tokens=2,3 delims== " %%a in ('set VAR') Do (
    echo xcopy "%%a" "%%b" /E /D /C /Y /I 
)
pause

答案 15 :(得分: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;
    }

}

答案 16 :(得分:0)

使用regex标志(不区分大小写的{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)
 * */

答案 17 :(得分:0)

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

答案 18 :(得分:-1)

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

String s1 = "AbBaCca";
String s2 = "bac";
String toLower = s1.toLowerCase();
return toLower.contains(s2);

答案 19 :(得分:-1)

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