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

说我有两个字符串,

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

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

 return s1.contains(s2); 

我很确定contains()是区分大小写的,但是我不能确定这是从阅读文档。 如果是的话,我想我最好的方法是这样的:

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

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

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

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

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

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

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

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

您可以使用

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

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

更快的实现:利用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 ms
  3. 1x toLowerCase()和包含()缓存子字符串: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 

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


分析测试代码

如果您对如何执行分析感兴趣,那么下面是完整的可运行应用程序:

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

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

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

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

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

当文档失败时, DrJava将会是一个非常简单的测试方法。 只需在“交互”窗口中输入几个测试用例,就可以知道。

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

如果您使用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(); } 

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

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

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

 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

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

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

 String s1 = "AbBaCca"; String s2 = "bac"; String toLower = s1.toLowerCase(); return toLower.contains(s2); 
 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"); } } } 

另一种在字符串中查找字符串的简单方法是STRING.INDEXOF()

  String str = new String("Welcome"); System.out.print("Found Index :" ); System.out.println(str.indexOf( 'o' )); 

找到索引:4

http://www.tutorialspoint.com/java/java_string_indexof.htm

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