Я столкнулся с еще более серьезной проблемой при поиске в тексте таких слов, как .NET
, C++
, C#
и C
. Можно подумать, что программисты будут знать лучше, чем называть язык чем-то, для чего трудно писать регулярные выражения.
Во всяком случае, это то, что я обнаружил (обобщенный в основном из http://www.regular -expressions.info , который является отличным сайтом): в большинстве разновидностей регулярных выражений символы, которые соответствуют коротким класс символов руки \w
- это символы, которые обрабатываются как символы слова по границам слова. Ява является исключением. Java поддерживает Unicode для \b
, но не для \w
. (Я уверен, что в то время для этого была веская причина).
\w
означает «символ слова». Он всегда соответствует символам ASCII [A-Za-z0-9_]
. Обратите внимание на включение подчеркивания и цифр (но не тире!). В большинстве разновидностей, которые поддерживают Unicode, \w
включает много символов из других сценариев. Существует много несоответствий относительно того, какие символы на самом деле включены. Буквы и цифры из букв алфавита и иероглифов, как правило, включены. Знаки препинания, отличные от символов подчеркивания и цифр, которые не являются цифрами, могут быть включены или не включены. XML-схема и XPath даже включают все символы в \w
. Но Java, JavaScript и PCRE соответствуют только символам ASCII с \w
.
Именно поэтому регулярное выражение на основе Java для поиска C++
, C#
или .NET
(даже если вы помните, что нужно избегать точки и плюсов) привинчено \b
.
Примечание: я не уверен, что делать с ошибками в тексте, например, когда кто-то не ставит пробел после точки в конце предложения. Я допустил это, но я не уверен, что это правильно.
В любом случае, в Java, если вы ищете текст для этих странно названных языков, вам нужно заменить \b
на знаки пробела и до и после, а также знаки препинания. Например:
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();
}
Тогда в вашем тесте или основной функции:
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));
P.S. Благодарю http://regexpal.com/, без которого мир регулярных выражений будет очень несчастным!