Mi sono imbattuto in un problema ancora peggio quando la ricerca di testo per parole come .NET
, C++
, C#
, eC
. Penseresti che i programmatori di computer saprebbero meglio che nominare una lingua per cui è difficile scrivere espressioni regolari.
Ad ogni modo, questo è quello che ho scoperto (riassunto principalmente da http://www.regular-expressions.info , che è un ottimo sito): nella maggior parte dei gusti di regex, i personaggi che corrispondono alla classe dei caratteri a mano corta \w
sono i caratteri che sono trattati come caratteri di parola dai limiti di parola. Java è un'eccezione. Java supporta Unicode per \b
ma non per \w
. (Sono sicuro che ci fosse una buona ragione per farlo al momento).
La \w
sta per "carattere di parola". Corrisponde sempre ai caratteri ASCII [A-Za-z0-9_]
. Notare l'inclusione del trattino basso e delle cifre (ma non trattino!). Nella maggior parte dei gusti che supportano Unicode, \w
include molti caratteri di altri script. C'è molta incoerenza su quali personaggi siano effettivamente inclusi. Sono generalmente incluse lettere e cifre di script alfabetici e ideografi. La punteggiatura del connettore diversa dal carattere di sottolineatura e dai simboli numerici che non sono cifre può o meno essere inclusa. XML Schema e XPath includono anche tutti i simboli in \w
. Ma Java, JavaScript e PCRE corrispondono solo a caratteri ASCII con \w
.
Quale è il motivo per Java-based ricerche regex per C++
, C#
o .NET
(anche quando si ricorda di fuggire il periodo e vantaggi) sono avvitati dal \b
.
Nota: non sono sicuro di cosa fare per gli errori nel testo, come quando qualcuno non mette uno spazio dopo un punto alla fine di una frase. L'ho permesso, ma non sono sicuro che sia necessariamente la cosa giusta da fare.
Ad ogni modo, in Java, se stai cercando del testo per quei linguaggi dai \b
nomi strani, devi sostituire quelli con spazi bianchi prima e dopo e i designatori di punteggiatura. Per esempio:
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();
}
Quindi nel test o nella funzione 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 I miei ringraziamenti a http://regexpal.com/ senza i quali il mondo regex sarebbe davvero infelice!