Je suis tombé sur un problème encore pire lorsque le texte recherche des mots comme .NET
, C++
, C#
et C
. On pourrait penser que les programmeurs informatiques sauraient mieux que de nommer un langage pour lequel il est difficile d'écrire des expressions régulières.
Quoi qu'il en soit, c'est ce que j'ai découvert (résumé principalement de http://www.regular-expressions.info , qui est un excellent site): Dans la plupart des versions de regex, les caractères qui correspondent à la classe de caractères abrégés \w
sont les les caractères qui sont traités comme des caractères de mots par des limites de mots. Java est une exception. Java prend en charge Unicode pour \b
mais pas pour \w
. (Je suis sûr qu'il y avait une bonne raison à cela à l'époque).
Le \w
signifie «caractère de mot». Il correspond toujours aux caractères ASCII [A-Za-z0-9_]
. Notez l'inclusion du trait de soulignement et des chiffres (mais pas du tiret!). Dans la plupart des versions qui prennent en charge Unicode, \w
inclut de nombreux caractères d'autres scripts. Il y a beaucoup d'incohérences sur les caractères réellement inclus. Les lettres et les chiffres des scripts alphabétiques et des idéogrammes sont généralement inclus. La ponctuation du connecteur autre que le trait de soulignement et les symboles numériques qui ne sont pas des chiffres peuvent ou non être inclus. XML Schema et XPath incluent même tous les symboles dans \w
. Mais Java, JavaScript et PCRE ne correspondent qu'aux caractères ASCII avec\w
.
C'est pourquoi les regex basées sur Java recherchent C++
, C#
ou .NET
(même si vous vous souvenez d'échapper au point et aux avantages) sont vissées par le\b
.
Remarque: je ne sais pas quoi faire en cas d'erreurs de texte, comme lorsque quelqu'un ne met pas d'espace après un point à la fin d'une phrase. Je l'ai permis, mais je ne suis pas sûr que ce soit nécessairement la bonne chose à faire.
Quoi qu'il en soit, en Java, si vous recherchez du texte pour ces langages aux noms étranges, vous devez remplacer le \b
par des indicateurs d'espaces et de ponctuation avant et après. Par exemple:
public static String grep(String regexp, String multiLineStringToSearch) {
String result = "";
String[] lines = multiLineStringToSearch.split("\\n");
Pattern pattern = Pattern.compile(regexp);
for (String line : lines) {
Matcher matcher = pattern.matcher(line);
if (matcher.find()) {
result = result + "\n" + line;
}
}
return result.trim();
}
Puis dans votre test ou fonction principale:
String beforeWord = "(\\s|\\.|\\,|\\!|\\?|\\(|\\)|\\'|\\\"|^)";
String afterWord = "(\\s|\\.|\\,|\\!|\\?|\\(|\\)|\\'|\\\"|$)";
text = "Programming in C, (C++) C#, Java, and .NET.";
System.out.println("text="+text);
// Here is where Java word boundaries do not work correctly on "cutesy" computer language names.
System.out.println("Bad word boundary can't find because of Java: grep with word boundary for .NET="+ grep("\\b\\.NET\\b", text));
System.out.println("Should find: grep exactly for .NET="+ grep(beforeWord+"\\.NET"+afterWord, text));
System.out.println("Bad word boundary can't find because of Java: grep with word boundary for C#="+ grep("\\bC#\\b", text));
System.out.println("Should find: grep exactly for C#="+ grep("C#"+afterWord, text));
System.out.println("Bad word boundary can't find because of Java:grep with word boundary for C++="+ grep("\\bC\\+\\+\\b", text));
System.out.println("Should find: grep exactly for C++="+ grep(beforeWord+"C\\+\\+"+afterWord, text));
System.out.println("Should find: grep with word boundary for Java="+ grep("\\bJava\\b", text));
System.out.println("Should find: grep for case-insensitive java="+ grep("?i)\\bjava\\b", text));
System.out.println("Should find: grep with word boundary for C="+ grep("\\bC\\b", text)); // Works Ok for this example, but see below
// Because of the stupid too-short cutsey name, searches find stuff it shouldn't.
text = "Worked on C&O (Chesapeake and Ohio) Canal when I was younger; more recently developed in Lisp.";
System.out.println("text="+text);
System.out.println("Bad word boundary because of C name: grep with word boundary for C="+ grep("\\bC\\b", text));
System.out.println("Should be blank: grep exactly for C="+ grep(beforeWord+"C"+afterWord, text));
// Make sure the first and last cases work OK.
text = "C is a language that should have been named differently.";
System.out.println("text="+text);
System.out.println("grep exactly for C="+ grep(beforeWord+"C"+afterWord, text));
text = "One language that should have been named differently is C";
System.out.println("text="+text);
System.out.println("grep exactly for C="+ grep(beforeWord+"C"+afterWord, text));
//Make sure we don't get false positives
text = "The letter 'c' can be hard as in Cat, or soft as in Cindy. Computer languages should not require disambiguation (e.g. Ruby, Python vs. Fortran, Hadoop)";
System.out.println("text="+text);
System.out.println("Should be blank: grep exactly for C="+ grep(beforeWord+"C"+afterWord, text));
PS Mes remerciements à http://regexpal.com/ sans qui le monde des regex serait très misérable!