Dis que j'ai deux ficelles,
String s1 = "AbBaCca";
String s2 = "bac";
Je veux effectuer une vérification en retournant que s2
est contenu dans s1
. Je peux le faire avec:
return s1.contains(s2);
Je suis à peu près sûr que contains()
est sensible à la casse, mais je ne peux pas le déterminer avec certitude en lisant la documentation. Si c'est le cas, je suppose que ma meilleure méthode serait quelque chose comme:
return s1.toLowerCase().contains(s2.toLowerCase());
Tout cela mis à part, existe-t-il un autre moyen (peut-être meilleur) d'accomplir cela sans se soucier de la sensibilité à la casse?
Oui, contient est sensible à la casse. Vous pouvez utiliser Java.util.regex.Pattern avec l'indicateur CASE_INSENSITIVE pour la correspondance sans distinction de casse:
Pattern.compile(Pattern.quote(wantedStr), Pattern.CASE_INSENSITIVE).matcher(source).find();
EDIT: Si s2 contient des caractères spéciaux regex (il y en a beaucoup), il est important de le citer en premier. J'ai corrigé ma réponse car c'est la première fois que les gens verront, mais votez pour Matt Quail depuis qu'il l'a signalé.
Un problème avec la réponse de Dave L. est lorsque s2 contient un balisage regex tel que \d
, etc.
Vous voulez appeler Pattern.quote () sur s2:
Pattern.compile(Pattern.quote(s2), Pattern.CASE_INSENSITIVE).matcher(s1).find();
Vous pouvez utiliser
org.Apache.commons.lang3.StringUtils.containsIgnoreCase("AbBaCca", "bac");
La bibliothèque Apache Commons est très utile pour ce genre de choses. Et celle-ci peut être meilleure que les expressions régulières, car les expressions rationnelles sont toujours chères en termes de performances.
String.regionMatches()
L'utilisation de l'expression rationnelle peut être relativement lente. Peu importe si vous voulez simplement vérifier dans un cas. Mais si vous avez un tableau ou une collection de milliers ou de centaines de milliers de chaînes, les choses peuvent devenir très lentes.
La solution présentée ci-dessous n'utilise pas d'expressions régulières ni toLowerCase()
(qui est également lente car elle crée d'autres chaînes et les jette simplement après la vérification).
La solution s'appuie sur la méthode String.regionMatches () qui semble être inconnue. Il vérifie si 2 régions String
correspondent, mais ce qui est important, c’est qu’il a également une surcharge avec un paramètre pratique 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;
}
Cette analyse de la vitesse ne signifie pas être sorcière, mais juste une idée approximative de la rapidité des différentes méthodes.
Je compare 5 méthodes.
String.contains()
.String.contains()
avec la sous-chaîne pré-mise en cache et en minuscule. Cette solution n’est déjà pas aussi flexible car elle teste une sous-chaîne de prédefiend.Pattern.compile().matcher().find()
...)Pattern
pré-créé et mis en cache. Cette solution n'est déjà pas aussi flexible car elle teste une sous-chaîne prédéfinie.Résultats (en appelant la méthode 10 millions de fois):
Pattern
: 1845 msRésultats dans un tableau:
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
Notre méthode est 4x plus rapide par rapport à la minuscule et en utilisant contains()
, 10x plus rapide par rapport à l'utilisation d'expressions régulières et également 3 fois plus vite même si le Pattern
est précaché (et perd de la flexibilité pour la recherche d'une sous-chaîne arbitraire) .
Si vous souhaitez savoir comment l'analyse a été effectuée, voici l'application complète exécutable:
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");
}
}
Une façon plus simple de procéder (sans se soucier de la correspondance des modèles) serait de convertir les deux String
s en minuscules:
String foobar = "fooBar";
String bar = "FOO";
if (foobar.toLowerCase().contains(bar.toLowerCase()) {
System.out.println("It's a match!");
}
Oui, cela est réalisable:
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;
Ce code renverra la chaîne "TRUE!" comme il a constaté que vos personnages ont été contenus.
Vous pouvez utiliser expressions régulières , et cela fonctionne:
boolean found = s1.matches("(?i).*" + s2+ ".*");
J'ai fait un test pour trouver une correspondance d'une chaîne insensible à la casse. J'ai un vecteur de 150 000 objets ayant tous une chaîne comme champ et voulant trouver le sous-ensemble correspondant à une chaîne. J'ai essayé trois méthodes:
Convertir tout en minuscule
for (SongInformation song: songs) {
if (song.artist.toLowerCase().indexOf(pattern.toLowercase() > -1) {
...
}
}
Utilisez la méthode String matches ()
for (SongInformation song: songs) {
if (song.artist.matches("(?i).*" + pattern + ".*")) {
...
}
}
Utilisez des expressions régulières
Pattern p = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher("");
for (SongInformation song: songs) {
m.reset(song.artist);
if (m.find()) {
...
}
}
Les résultats de chronométrage sont:
Aucune tentative de correspondance: 20 ms
Pour diminuer la correspondance: 182 ms
Correspondance de chaîne: 278 ms
Expression régulière: 65 ms
L'expression régulière semble être la plus rapide pour ce cas d'utilisation.
Voici des exemples compatibles avec Unicode que vous pouvez créer en intégrant ICU4j. J'imagine que "ignorer la casse" est discutable pour les noms de méthodes car, bien que les comparaisons de force principales ignorent la casse, elles sont décrites comme étant les spécificités dépendantes de la localisation. Mais, espérons-le, cela dépend des paramètres régionaux de la manière attendue par l'utilisateur.
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();
}
Je ne sais pas quelle est la principale question que vous posez ici, mais oui, .contains est sensible à la casse.
"AbCd".toLowerCase().contains("abcD".toLowerCase())
Nous pouvons utiliser stream avec anyMatch et contient 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;
}
}
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;
}
Fondamentalement, c'est une méthode qui prend deux chaînes. Il est supposé être une version non sensible à la casse de contient (). Lorsque vous utilisez la méthode contient, vous voulez voir si une chaîne est contenue dans l'autre.
Cette méthode prend la chaîne "sub" et vérifie si elle est égale aux sous-chaînes de la chaîne de conteneur de longueur égale à "sub". Si vous regardez la boucle for
, vous verrez qu’elle se répète dans les sous-chaînes (qui sont la longueur du "sub") sur la chaîne de conteneur.
Chaque itération vérifie si la sous-chaîne de la chaîne de conteneur est equalsIgnoreCase
pour le sous-fichier.
Si vous devez rechercher une chaîne ASCII dans une autre chaîne ASCII, telle qu'un URL , vous constaterez que ma solution est meilleure. J'ai testé la méthode et la mienne d'icza pour la vitesse et voici les résultats:
Le code:
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));
}
Il existe un moyen simple et concis d’utiliser regex (insensible à la casse {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)
* */
ou vous pouvez utiliser une approche simple et simplement convertir le cas de la chaîne en cas de la sous-chaîne, puis utiliser la méthode contient.
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");
}
}
}