Wpadłem na jeszcze gorszym problemem podczas wyszukiwania tekstu na słowa takie jak .NET
, C++
, C#
, i C
. Można by pomyśleć, że programiści komputerowi wiedzieliby lepiej, niż nazywać język czymś, dla którego trudno jest pisać wyrażenia regularne.
Tak czy inaczej, oto co się dowiedziałem (podsumowane głównie z http://www.regular-expressions.info , która jest świetną stroną): W większości odmian wyrażenia regularnego znaki, które są dopasowywane przez klasę znaków krótkiej ręki \w
to znaki, które są traktowane jako znaki słowa według granic słów. Java jest wyjątkiem. Java obsługuje Unicode dla, \b
ale nie dla \w
. (Jestem pewien, że wtedy był ku temu dobry powód).
\w
Oznacza „charakteru słowa”. Zawsze pasuje do znaków ASCII [A-Za-z0-9_]
. Zwróć uwagę na podkreślenie i cyfry (ale nie myślnik!). W większości wersji obsługujących Unicode \w
zawiera wiele znaków z innych skryptów. Istnieje wiele niespójności co do tego, które postacie są faktycznie uwzględnione. Zwykle uwzględniane są litery i cyfry ze skryptów alfabetycznych i ideogramów. Interpunkcja łącznika, inna niż podkreślenie i symbole numeryczne, które nie są cyframi, może, ale nie musi, zostać uwzględniona. Schemat XML i XPath zawierają nawet wszystkie symbole w \w
. Ale Java, JavaScript i PCRE dopasowują tylko znaki ASCII z \w
.
Dlatego Java regex wyszukuje C++
, C#
lub .NET
(nawet jeśli pamiętać, aby uciec od okresu i plusy) przykręcone są przez \b
.
Uwaga: nie jestem pewien, co zrobić z błędami w tekście, na przykład gdy ktoś nie wstawia spacji po kropce na końcu zdania. Pozwoliłem na to, ale nie jestem pewien, czy koniecznie jest to właściwe postępowanie.
W każdym razie, w Javie, jeśli szukasz tekstu dla tych dziwnie nazwanych języków, musisz zamienić na \b
przed i po białych znakach i znakach interpunkcyjnych. Na przykład:
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();
}
Następnie w teście lub funkcji głównej:
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 Moje podziękowania dla http://regexpal.com/, bez którego świat regexów byłby bardzo nieszczęśliwy!