Corri para um problema ainda pior quando procurar texto para palavras como .NET
, C++
, C#
, eC
. Você pensaria que os programadores de computador saberiam melhor do que nomear uma linguagem como algo difícil de escrever expressões regulares.
De qualquer forma, foi o que eu descobri (resumido principalmente em http://www.regular-expressions.info , que é um ótimo site): Na maioria dos tipos de regex, os caracteres correspondentes à classe de caracteres abreviados \w
são os caracteres que são tratados como caracteres de palavra pelos limites da palavra. Java é uma exceção. Java suporta Unicode para \b
mas não para \w
. (Tenho certeza de que havia uma boa razão para isso no momento).
A \w
significa "caráter palavra". Sempre corresponde aos caracteres ASCII [A-Za-z0-9_]
. Observe a inclusão de sublinhado e dígitos (mas não traço!). Na maioria dos tipos de suporte ao Unicode, \w
inclui muitos caracteres de outros scripts. Há muita inconsistência sobre quais caracteres estão realmente incluídos. Letras e dígitos de scripts e ideogramas alfabéticos geralmente estão incluídos. A pontuação do conector, exceto os símbolos de sublinhado e numéricos que não são dígitos, pode ou não ser incluída. Esquema XML e XPath incluem até todos os símbolos \w
. Mas Java, JavaScript e PCRE correspondem apenas a caracteres ASCII com\w
.
É por isso que pesquisas regex Java baseada em para C++
, C#
ou .NET
(mesmo quando você se lembrar de escapar do período e vantagens) são parafusados pelo\b
.
Nota: Não tenho certeza do que fazer com relação a erros no texto, como quando alguém não coloca um espaço depois de um ponto no final de uma frase. Eu permiti, mas não tenho certeza de que seja necessariamente a coisa certa a fazer.
De qualquer forma, em Java, se você estiver pesquisando texto para essas linguagens de nomes estranhos, precisará substituir o \b
por antes e depois dos designadores de espaço em branco e pontuação. Por exemplo:
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();
}
Então, em seu teste ou função 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));
PS Meus agradecimentos a http://regexpal.com/ sem os quais o mundo regex seria muito infeliz!