正则expression式中的单词边界是什么?
我在Java 1.6中使用Java正则expression式(特别是parsing数字输出),并且找不到\b
(“字边界”)的精确定义。 我假定-12
将是一个“整数字”(匹配\b\-?\d+\b
),但似乎这是行不通的。 我很想知道如何匹配空格分隔的数字。
例:
Pattern pattern = Pattern.compile("\\s*\\b\\-?\\d+\\s*"); String plus = " 12 "; System.out.println(""+pattern.matcher(plus).matches()); String minus = " -12 "; System.out.println(""+pattern.matcher(minus).matches()); pattern = Pattern.compile("\\s*\\-?\\d+\\s*"); System.out.println(""+pattern.matcher(minus).matches());
这返回:
true false true
在大多数正则expression式语言中,单词边界是\w
和\W
(非单词char)之间的位置,或者在string的开始或结尾(如果它分别以单词字符( [0-9A-Za-z_]
)。
所以,在string"-12"
,它将在1之前或之后匹配。短划线不是单词字符。
字边界可以出现在三个位置之一:
- 在string中的第一个字符之前,如果第一个字符是单词字符。
- 在string中的最后一个字符之后,如果最后一个字符是一个单词字符。
- string中的两个字符之间,其中一个是单词字符,另一个不是单词字符。
单词字符是字母数字; 减号不是。 采取正则expression式教程 。
查看关于边界条件的文档:
http://java.sun.com/docs/books/tutorial/essential/regex/bounds.html
看看这个例子:
public static void main(final String[] args) { String x = "I found the value -12 in my string."; System.err.println(Arrays.toString(x.split("\\b-?\\d+\\b"))); }
打印出来时,请注意输出是这样的:
[我发现了价值 – 在我的string。]
这意味着“ – ”字符不会被认为是在单词的边界上,因为它不被认为是单词字符。 看起来像@ brianary有点殴打我的一拳,所以他得到了一个投票。
我讨论了\b
样式正则expression式边界实际上在这里 。
小故事是他们是有条件的 。 他们的行为取决于他们在旁边。
# same as using a \b before: (?(?=\w) (?<!\w) | (?<!\W) ) # same as using a \b after: (?(?<=\w) (?!\w) | (?!\W) )
有时候这不是你想要的。 看到我的其他答案进行阐述。
当search文本的单词,如.NET
, C++
, C#
和C
时遇到了更糟的问题。 你会认为,计算机程序员会知道比命名一个语言难以编写正则expression式的东西。
无论如何,这是我发现的(主要来自http://www.regular-expressions.info ,这是一个很棒的网站):在大多数正则expression式中,短符号类匹配的字符\w
是通过单词边界被视为单词字符的字符。 Java是一个例外。 Java支持\b
Unicode,但不支持\w
。 (我确信当时有一个很好的理由)。
\w
代表“单词字符”。 它总是匹配ASCII字符[A-Za-z0-9_]
。 注意包含下划线和数字(但不包括短划线!)。 在大多数支持Unicode的版本中, \w
包含来自其他脚本的许多字符。 实际上包含哪些字符有很多不一致的地方。 通常包括来自字母脚本和表意文字的字母和数字。 不包含数字的下划线和数字符号以外的连接符号可能包含或可能不包含。 XML Schema和XPath甚至包含\w
所有符号。 但是,Java,JavaScript和PCRE只能与\w
匹配ASCII字符。
这就是为什么基于Java的正则expression式searchC++
, C#
或.NET
(即使你记得要跳过句点和加号)的原因是\b
。
注意:我不确定如何处理文本中的错误,比如某人在句子结束后没有放置空格。 我允许这样做,但我不确定这是否是正确的做法。
无论如何,在Java中,如果您正在为那些古怪的语言search文本,则需要用空格和标点符号前后的replace\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(); }
然后在你的testing或主要function:
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 (eg 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));
我相信你的问题是由于这个事实-
不是一个字的字符。 因此,单词边界将在-
之后匹配,因此不会捕获它。 单词边界在string中的第一个和最后一个字符之前匹配,以及在它之前是单词字符或非单词字符之前的任何地方,并且之后是相反的。 还要注意,单词边界是零宽度匹配。
一个可能的select是
(?:(?:^|\s)-?)\d+\b
这将匹配任何以空格字符和可选短划线开头的数字,并以单词边界结束。 它也将匹配从string开头开始的一个数字。
单词边界是一个位置,前面是一个单词字符,后面跟一个单词,后面跟一个单词字符,前面没有一个单词。
我认为这是最后一场比赛的边界(即字符跟随)或者string的开始或结束。