如何检查一个字符串是否包含另一个字符串在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种方法。
- 我们的containsIgnoreCase()方法。
- 通过将这两个字符串转换为小写,并调用
String.contains()
。 - 通过将源字符串转换为小写字母并使用预缓存的小写字符串调用
String.contains()
。 这个解决方案已经不那么灵活了,因为它测试了预定义的子字符串。 - 使用正则表达式(接受的答案
Pattern.compile().matcher().find()
…) - 使用正则表达式,但与预先创建和缓存
Pattern
。 这个解决方案已经不够灵活,因为它测试了一个预定义的子字符串。
结果(通过调用方法1000万次):
- 我们的方法:670毫秒
- 2x toLowerCase()和contains():2829 ms
- 1x toLowerCase()和包含()缓存子字符串:2446毫秒
- 正则表达式:7180毫秒
- 正则表达式与缓存
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万的对象的所有与一个字符串作为一个字段的矢量,并希望找到匹配一个字符串的子集。 我尝试了三种方法:
-
全部转换为小写
for (SongInformation song: songs) { if (song.artist.toLowerCase().indexOf(pattern.toLowercase() > -1) { ... } }
-
使用String matches()方法
for (SongInformation song: songs) { if (song.artist.matches("(?i).*" + pattern + ".*")) { ... } }
-
使用正则表达式
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
String x="abCd"; System.out.println(Pattern.compile("c",Pattern.CASE_INSENSITIVE).matcher(x).find());