Me encontré con un problema aún peor cuando la búsqueda de texto de palabras como .NET
, C++
, C#
, yC
. Se podría pensar que los programadores de computadoras sabrían mejor que nombrar un idioma para lo cual es difícil escribir expresiones regulares.
De todos modos, esto es lo que descubrí (resumido principalmente en http://www.regular-expressions.info , que es un gran sitio): en la mayoría de los sabores de expresiones regulares, los caracteres que coinciden con la clase de caracteres abreviados \w
son los caracteres que se tratan como caracteres de palabras por límites de palabras. Java es una excepción. Java admite Unicode para \b
pero no para \w
. (Estoy seguro de que había una buena razón para ello en ese momento).
Las \w
siglas de "carácter de palabra". Siempre coincide con los caracteres ASCII [A-Za-z0-9_]
. Observe la inclusión del guión bajo y los dígitos (¡pero no el guión!). En la mayoría de los sabores que admiten Unicode, \w
incluye muchos caracteres de otros scripts. Hay mucha inconsistencia sobre qué personajes están realmente incluidos. Generalmente se incluyen letras y dígitos de scripts alfabéticos e ideógrafos. Los signos de puntuación del conector que no sean el subrayado y los símbolos numéricos que no son dígitos pueden o no estar incluidos. El esquema XML y XPath incluso incluyen todos los símbolos \w
. Pero Java, JavaScript y PCRE solo coinciden con los caracteres ASCII \w
.
Es por eso que las búsquedas de expresiones regulares basadas en Java buscan C++
, C#
o .NET
(incluso cuando recuerdas escapar del período y las ventajas) se atornillan\b
.
Nota: No estoy seguro de qué hacer con los errores en el texto, como cuando alguien no pone un espacio después de un punto al final de una oración. Lo permití, pero no estoy seguro de que sea necesariamente lo correcto.
De todos modos, en Java, si está buscando texto para esos lenguajes con nombres extraños, debe reemplazarlo \b
con espacios en blanco y designadores de puntuación antes y después. Por ejemplo:
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();
}
Luego, en su prueba o función principal:
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));
PD: ¡Gracias a http://regexpal.com/ sin el cual el mundo regex sería muy miserable!