How to check if a String contains another String in a case insensitive manner in Java?
Как проверить, содержит ли строка другую строку без учета регистра в Java?
Допустим, у меня есть две строки,
Strings1="AbBaCca"; Strings2="bac";
Я хочу выполнить проверку, возвращающую то, что s2 содержится внутри s1. Я могу сделать это с помощью:
return s1.contains(s2);
Я почти уверен, что contains() чувствителен к регистру, однако я не могу определить это наверняка, прочитав документацию. Если это так, то я полагаю, что моим лучшим методом было бы что-то вроде:
Помимо всего этого, есть ли другой (возможно, лучший) способ добиться этого, не заботясь о чувствительности к регистру?
Переведено автоматически
Ответ 1
Да, параметр contains чувствителен к регистру. Вы можете использовать java.util.regex.Pattern с флагом CASE_INSENSITIVE для сопоставления без учета регистра:
РЕДАКТИРОВАТЬ: Если s2 содержит специальные символы регулярного выражения (которых много), важно сначала заключить его в кавычки. Я исправил свой ответ, поскольку это первый, который люди увидят, но проголосуйте за ответ Мэтта Куэйла, поскольку он указал на это.
Ответ 2
Одна из проблем с ответом Дейва Л. заключается в том, что s2 содержит разметку регулярных выражений, такую как \d и т.д.
Библиотека Apache Commons очень полезна для такого рода задач. И эта конкретная библиотека может быть лучше регулярных выражений, поскольку регулярное выражение всегда дорого с точки зрения производительности.
Ответ 4
Более быстрая реализация: использование String.regionMatches()
Использование регулярного выражения может быть относительно медленным. Это (быть медленным) не имеет значения, если вы просто хотите проверить в одном регистре. Но если у вас есть массив или коллекция из тысяч или сотен тысяч строк, работа может идти довольно медленно.
Представленное ниже решение также не использует регулярные выражения toLowerCase() (что также является медленным, потому что оно создает другие строки и просто выбрасывает их после проверки).
Решение основано на методе String.regionMatches(), который кажется неизвестным. Он проверяет, совпадают ли 2 String области, но важно то, что он также перегружен удобным ignoreCase параметром.
publicstaticbooleancontainsIgnoreCase(String src, String what) { finalintlength= what.length(); if (length == 0) returntrue; // Empty string is contained
for (inti= src.length() - length; i >= 0; i--) { // Quick check before calling the more expensive regionMatches() method: finalcharch= src.charAt(i); if (ch != firstLo && ch != firstUp) continue;
if (src.regionMatches(true, i, what, 0, length)) returntrue; }
returnfalse; }
Анализ скорости
Этот анализ скорости не является наукой о ракетостроении, это просто приблизительная картина того, насколько быстры различные методы.
Я сравниваю 5 методов.
Наш метод containsIgnoreCase().
Путем преобразования обеих строк в нижний регистр и вызова String.contains().
Путем преобразования исходной строки в нижний регистр и вызова String.contains() с предварительно кэшированной подстрокой в нижнем регистре. Это решение уже не такое гибкое, потому что оно проверяет заданную подстроку.
Используя регулярное выражение (принятый ответ Pattern.compile().matcher().find() ...)
Используя регулярное выражение, но с предварительно созданным и кэшированнымPattern. Это решение уже не такое гибкое, потому что оно проверяет предопределенную подстроку.
Результаты (при вызове метода 10 миллионов раз):
Наш метод: 670 мс
2 раза в нижнем регистре() и содержит(): 2829 мс
1x toLowerCase() и contains() с кэшированной подстрокой: 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 раз быстрее по сравнению с использованием регулярных выражений, а также в 3 раза быстрее, даже если Pattern предварительно кэширован (и теряется гибкость проверки произвольной подстроки).
Анализ тестового кода
Если вам интересно, как проводился анализ, вот полное работоспособное приложение:
import java.util.regex.Pattern;
publicclassContainsAnalysis {
// Case 1 utilizing String.regionMatches() publicstaticbooleancontainsIgnoreCase(String src, String what) { finalintlength= what.length(); if (length == 0) returntrue; // Empty string is contained
for (inti= src.length() - length; i >= 0; i--) { // Quick check before calling the more expensive regionMatches() // method: finalcharch= src.charAt(i); if (ch != firstLo && ch != firstUp) continue;
if (src.regionMatches(true, i, what, 0, length)) returntrue; }
returnfalse; }
// Case 2 with 2x toLowerCase() and contains() publicstaticbooleancontainsConverting(String src, String what) { return src.toLowerCase().contains(what.toLowerCase()); }
// The cached substring for case 3 privatestaticfinalStringS="i am".toLowerCase();
// Case 3 with pre-cached substring and 1x toLowerCase() and contains() publicstaticbooleancontainsConverting(String src) { return src.toLowerCase().contains(S); }
// Case 4 with regexp publicstaticbooleancontainsIgnoreCaseRegexp(String src, String what) { return Pattern.compile(Pattern.quote(what), Pattern.CASE_INSENSITIVE) .matcher(src).find(); }
// The cached pattern for case 5 privatestaticfinalPatternP= Pattern.compile( Pattern.quote("i am"), Pattern.CASE_INSENSITIVE);
// Case 5 with pre-cached Pattern publicstaticbooleancontainsIgnoreCaseRegexp(String src) { return P.matcher(src).find(); }
// Main method: perfroms speed analysis on different contains methods // (case ignored) publicstaticvoidmain(String[] args)throws Exception { finalStringsrc="Hi, I am Adam"; finalStringwhat="i am";
long start, end; finalintN=10_000_000;
start = System.nanoTime(); for (inti=0; i < N; i++) containsIgnoreCase(src, what); end = System.nanoTime(); System.out.println("Case 1 took " + ((end - start) / 1000000) + "ms");
start = System.nanoTime(); for (inti=0; i < N; i++) containsConverting(src, what); end = System.nanoTime(); System.out.println("Case 2 took " + ((end - start) / 1000000) + "ms");
start = System.nanoTime(); for (inti=0; i < N; i++) containsConverting(src); end = System.nanoTime(); System.out.println("Case 3 took " + ((end - start) / 1000000) + "ms");
start = System.nanoTime(); for (inti=0; i < N; i++) containsIgnoreCaseRegexp(src, what); end = System.nanoTime(); System.out.println("Case 4 took " + ((end - start) / 1000000) + "ms");
start = System.nanoTime(); for (inti=0; i < N; i++) containsIgnoreCaseRegexp(src); end = System.nanoTime(); System.out.println("Case 5 took " + ((end - start) / 1000000) + "ms"); }