正则表达式中的单词边界是什么?


136

我在Java 1.6中使用Java正则表达式(解析数字输出,除其他用途外),但找不到\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

您可以发表一个包含输入和预期输出的小例子吗?
布伦特写代码

示例模式pattern = Pattern.compile(“ \\ s * \\ b \\-?? \\ d + \\ s *”); 字符串加=“ 12”; System.out.println(“” + pattern.matcher(plus).matches()); 字符串减=“ -12”; System.out.println(“” + pattern.matcher(minus).matches()); pattern = Pattern.compile(“ \\ s * \\-?? \\ d + \\ s *”); System.out.println(“” + pattern.matcher(minus).matches()); 给定:true false true
peter.murray.rust

Answers:


97

在大多数正则表达式中,单词边界是\w和之间的位置\W(非单词char),或者(如果分别)以单词字符([0-9A-Za-z_])开头或结尾,则位于字符串的开头或结尾。

因此,在字符串中"-12",它将与1之前或2之后匹配。破折号不是文字字符。


34
更正。\b是一个零宽度的断言,如果\w一侧存在,而另一侧存在\W,或者位置在字符串的开头或结尾,则匹配。\w随意定义为“标识符”字符(数字和下划线),而不是对英语特别有用的任何字符。
hobbs

100%正确。抱歉,不只是对您的内容发表评论。在看到您的答案之前,我点击了“提交”。
布伦特写代码

5
为了理解起见,是有可能重写正则表达式\bhello\b,而无需使用\b(使用\w\W和其它)?
David Portabella

5
排序方式:(^|\W)hello($|\W),除了它不会捕获之前和之后的任何非单词字符外,因此更像是(^|(?<=\W))hello($|(?=\W))(使用先行/后置断言)。
brianary

6
@brianary稍微简单一些:(?<!\w)hello(?!\w)
David Knipe '11

28

单词边界可以出现在以下三个位置之一:

  1. 如果字符串中的第一个字符是单词字符,则在字符串中第一个字符之前。
  2. 如果字符串中的最后一个字符是单词字符,则在字符串的最后一个字符之后。
  3. 字符串中的两个字符之间,其中一个是单词字符,另一个不是单词字符。

文字字符为字母数字;减号不是。摘自Regex教程


21

在学习正则表达式的过程中,我真的陷入了元字符\b。当我反复地问自己“ 它是什么,它是什么 ”时我确实没有理解它的含义。通过使用该网站进行一些尝试之后,我注意到单词的每个开头和结尾处的粉红色竖线都是虚线。当时我的意思很好。现在恰好是word(\w)-boundary

我的观点仅仅是要以理解为导向。它背后的逻辑应该从另一个答案中进行检验。

在此处输入图片说明


3
一个很好的站点,了解什么是单词边界以及如何进行匹配
vsingh

2
该帖子值得称赞,而不是展示。一幅图片价值一千个字。
M_M

13

单词边界是一个位置,该位置或者是单词字符的前面,而不是一个字符,或者是单词字符的后面,并且没有一个字符。


8

我在说什么\b样式的正则表达式边界实际上在这里

简短的故事是,它们是有条件的。他们的行为取决于他们所处的位置。

# same as using a \b before:
(?(?=\w) (?<!\w)  | (?<!\W) )

# same as using a \b after:
(?(?<=\w) (?!\w)  | (?!\W)  )

有时那不是您想要的。请参阅我的其他答案以进行详细说明。


7

我想解释一下艾伦·摩尔的答案

单词边界是一个位置,在该位置之前或之后没有单词,或者后面没有单词或没有单词。

假设我有一个字符串“这是一个 ç 一个 T,和她是一个 wesome”,而我应该替换所有出现(S)字母“A”只有在这封信存在于“一词的边界”,即a“ cat”中的字母不应替换。

因此,我将执行regex(在Python中)为

re.sub("\ba","e", myString.strip())//替换ae

所以输出将是这是eÇ 一个牛逼e第二,她的ewesome


5

搜索文本时的话喜欢我遇到了一个更糟糕的问题.NETC++C#,和C。您可能会认为,计算机程序员比为某种语言编写难于为其编写正则表达式的语言更好地了解。

无论如何,这就是我发现的内容(主要从http://www.regular-expressions.info进行总结,该网站是一个不错的网站):在大多数regex风格中,与简写字符类匹配的字符\w都是被单词边界视为单词字符的字符。Java是一个例外。Java支持Unicode,\b但不支持\w。(我确信当时有充分的理由)。

\w代表“单词字符”。它始终与ASCII字符匹配[A-Za-z0-9_]。请注意包含下划线和数字(但不包括破折号!)。在大多数支持Unicode的版本中,都\w包含许多其他脚本中的字符。关于实际包含哪些字符有很多不一致之处。通常包括字母脚本和表意文字的字母和数字。除下划线和非数字符号之外的连接器标点符号可能会包含在内,也可能不包含。XML Schema和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));

PS:我感谢http://regexpal.com/,没有它们,正则表达式世界将非常痛苦!


我努力地试图理解为什么我无法C#与之匹敌,但现在更清楚了
Mugoma J. Okomba

4

查看有关边界条件的文档:

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")));
    }

打印出来时,请注意输出是这样的:

[我在字符串中找到了值-。]

这意味着“-”字符不会被当作单词的边界来拾取,因为它不被视为单词字符。看起来@brianary有点打败我,所以他获得了赞成票。



1

我认为您的问题是由于-不是文字字符造成的。因此,单词border将在之后匹配-,因此不会捕获它。单词边界匹配字符串中的第一个单词字符和最后一个单词字符之后,以及在它之前是单词字符或非单词字符以及之后相反的任何位置。另请注意,单词边界是零宽度匹配。

一种可能的替代方法是

(?:(?:^|\s)-?)\d+\b

这将匹配任何以空格字符和可选破折号开始,并在单词边界处结束的数字。它还将匹配从字符串开头开始的数字。


0

我认为这是最后一个匹配项的边界(即字符后面)或字符串的开头或结尾。


1
您在考虑\G\A在第一次尝试匹配时匹配字符串的开头(例如);之后,它匹配上一个比赛结束的位置。
艾伦·摩尔

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.