Como verificar se uma String contém outra String em uma maneira que não diferencia maiúsculas e minúsculas em Java?

Digamos que eu tenha duas cordas

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

Eu quero executar uma verificação retornando que s2 está contido em s1 . Eu posso fazer isso com:

 return s1.contains(s2); 

Tenho certeza de que o contains() é sensível a maiúsculas e minúsculas, no entanto, não posso determinar isso com certeza lendo a documentação. Se for então, suponho que meu melhor método seria algo como:

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

Tudo isso de lado, existe outra maneira (possivelmente melhor) de conseguir isso sem se preocupar com a diferenciação de maiúsculas e minúsculas?

Sim, contém é sensível a maiúsculas e minúsculas. Você pode usar java.util.regex.Pattern com o sinalizador CASE_INSENSITIVE para correspondência insensível a maiúsculas e minúsculas:

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

EDIT: Se s2 contém caracteres especiais regex (dos quais existem muitos) é importante citá-lo primeiro. Eu corrigi a minha resposta, já que é a primeira que as pessoas vão ver, mas vote no Matt Quail desde que ele apontou isso.

Um problema com a resposta de Dave L. é quando s2 contém marcação de regex como \d , etc.

Você quer chamar Pattern.quote () em s2:

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

Você pode usar

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

A biblioteca Apache Commons é muito útil para esse tipo de coisa. E esse em particular pode ser melhor do que expressões regulares, pois o regex é sempre caro em termos de desempenho.

Uma implementação mais rápida: utilizando String.regionMatches()

O uso de regexp pode ser relativamente lento. (Sendo lento) não importa se você quer apenas verificar em um caso. Mas se você tiver um array ou uma coleção de milhares ou centenas de milhares de strings, as coisas podem ficar bem lentas.

A solução apresentada abaixo não usa expressões regulares nem toLowerCase() (que também é lento porque cria outras strings e simplesmente as joga fora depois da verificação).

A solução baseia-se no método String.regionMatches () , que parece ser desconhecido. Ele verifica se duas regiões String correspondem, mas o que é importante é que também tem uma sobrecarga com um parâmetro ignoreCase útil.

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

Análise rápida

Esta análise de velocidade não significa ser ciência de foguetes, apenas uma imagem grosseira de quão rápidos são os diferentes methods.

Eu comparo 5 methods.

  1. Nosso método containsIgnoreCase () .
  2. Convertendo ambas as strings para minúsculas e chame String.contains() .
  3. Convertendo cadeia de origem para minúscula e chamar String.contains() com a substring pré-armazenada em cache, lower-cased. Essa solução já não é tão flexível porque testa uma substring predefinida.
  4. Usando expressão regular (a resposta aceita Pattern.compile().matcher().find() …)
  5. Usando expressão regular, mas com Pattern pré-criado e em cache. Essa solução já não é tão flexível porque testa uma substring predefinida.

Resultados (chamando o método 10 milhões de vezes):

  1. Nosso método: 670 ms
  2. 2x toLowerCase () e contém (): 2829 ms
  3. 1x toLowerCase () e contains () com substring em cache: 2446 ms
  4. Regexp: 7180 ms
  5. Regexp com Pattern cache: 1845 ms

Resultados em uma tabela:

  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 

Nosso método é 4x mais rápido em comparação com letras minúsculas e usando contains() , 10x mais rápido em comparação ao uso de expressões regulares e também 3x mais rápido, mesmo que o Pattern seja pré-armazenado em cache (e perder a flexibilidade de verificar uma substring arbitrária).


Código de teste de análise

Se você estiver interessado em como a análise foi realizada, aqui está o aplicativo completo executável:

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

Sim, isso é possível:

 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; 

Este código retornará a string “TRUE!” como descobriu que seus personagens estavam contidos.

Uma maneira mais simples de fazer isso (sem se preocupar com a correspondência de padrões) seria converter ambas as String em minúsculas:

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

Você pode usar expressões regulares e funciona:

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

Aqui estão alguns que podem ser feitos com Unicode se você puxar o ICU4j. Eu acho que “ignorar maiúsculas e minúsculas” é questionável para os nomes de método porque, embora as comparações de força primária ignorem maiúsculas e minúsculas, ele é descrito como as especificidades sendo dependente de localidade. Mas esperamos que ele seja dependente do local de uma maneira que o usuário esperaria.

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

Fiz um teste ao encontrar uma correspondência de uma string sem distinção entre maiúsculas e minúsculas. Eu tenho um vetor de 150.000 objects, todos com um String como um campo e queria encontrar o subconjunto que corresponde a uma string. Eu tentei três methods:

  1. Converte todos para minúsculas

     for (SongInformation song: songs) { if (song.artist.toLowerCase().indexOf(pattern.toLowercase() > -1) { ... } } 
  2. Use o método String matches ()

     for (SongInformation song: songs) { if (song.artist.matches("(?i).*" + pattern + ".*")) { ... } } 
  3. Use expressões regulares

     Pattern p = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE); Matcher m = p.matcher(""); for (SongInformation song: songs) { m.reset(song.artist); if (m.find()) { ... } } 

Os resultados do tempo são:

  • Nenhuma tentativa de correspondência: 20 ms

  • Para diminuir o jogo: 182 ms

  • Jogos de cordas: 278 ms

  • Expressão regular: 65 ms

A expressão regular parece ser a mais rápida para este caso de uso.

Não sei qual é a sua principal questão aqui, mas sim, .contains é sensível a maiúsculas e minúsculas.

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

Basicamente, é um método que leva duas seqüências de caracteres. É suposto ser uma versão não sensível a maiúsculas e minúsculas de contains (). Ao usar o método contains, você deseja ver se uma string está contida na outra.

Este método usa a string que é "sub" e verifica se é igual ao substrings da string do container que são iguais em comprimento ao "sub". Se você olhar para o loop for , verá que itera em substrings (que são o comprimento do "sub") sobre a string do contêiner.

Cada iteração verifica se a subseqüência da string do contêiner é equalsIgnoreCase a equalsIgnoreCase para o sub.

Se você tiver que procurar uma string ASCII em outra string ASCII, como uma URL , você achará minha solução melhor. Eu testei o método do icza e o meu para a velocidade e aqui estão os resultados:

  • Caso 1 levou 2788 ms – regionMatches
  • Caso 2 levou 1520 ms – meu

O código:

 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= '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)); } 
 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 x="abCd"; System.out.println(Pattern.compile("c",Pattern.CASE_INSENSITIVE).matcher(x).find()); 

Você poderia simplesmente fazer algo assim:

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

Outro método fácil de usar para encontrar uma string dentro de uma string é STRING.INDEXOF ()

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

Encontrado Índice: 4

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

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