在文本中搜索 .NET、C++、C# 和 C 等词时,我遇到了更严重的问题。你会认为计算机程序员会比给一种难以编写正则表达式的语言命名更好。
无论如何,这就是我发现的(主要来自 http://www.regular-expressions.info 的总结,这是一个很棒的网站):在大多数正则表达式中,与简写字符类 \w 匹配的字符是被单词边界视为单词字符。 Java 是个例外。 Java 支持\b 的Unicode,但不支持\w。 (我确信当时有充分的理由)。
\w 代表“单词字符”。它始终匹配 ASCII 字符 [A-Za-z0-9_]。注意包含下划线和数字(但不是破折号!)。在大多数支持 Unicode 的风格中,\w 包含许多来自其他脚本的字符。关于实际包含哪些字符存在很多不一致之处。通常包括来自字母脚本和表意文字的字母和数字。除了下划线和不是数字的数字符号之外的连接标点符号可能包含也可能不包含。 XML Schema 和 XPath 甚至包括\w 中的所有符号。但是 Java、JavaScript 和 PCRE 仅匹配带有 \w 的 ASCII 字符。
这就是为什么基于 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));
附:感谢http://regexpal.com/,没有他,正则表达式的世界将会非常悲惨!