【问题标题】:How exactly do Regular Expression word boundaries work in PHP?正则表达式单词边界在 PHP 中究竟是如何工作的?
【发布时间】:2011-06-30 08:01:51
【问题描述】:

我目前正在编写一个库,用于匹配内容中的特定单词。

基本上它的工作方式是将单词编译成正则表达式,并通过所述正则表达式运行内容。

我要添加的功能是指定要匹配的给定单词是否必须开始和/或结束一个单词。例如,我有单词cat。我指定它必须开始一个单词,所以catering匹配,因为cat 在开头,但ducat 不会匹配 因为cat 没有开始这个词。

我想使用word boundaries 执行此操作,但在一些测试中我发现它并没有像我预期的那样工作。

采取以下,

preg_match("/(^|\b)@nimal/i", "something@nimal", $match);
preg_match("/(^|\b)@nimal/i", "something!@nimal", $match);

在上面的陈述中,我希望得到以下结果,

> false
> 1 (@nimal)

但结果却恰恰相反,

> 1 (@nimal)
> false

首先,我预计它会失败,因为该组将吃掉@,让nimal@nimal 匹配,显然它不会。相反,该组匹配一个空字符串,因此匹配@nimal,这意味着@ 被认为是单词的一部分。

在第二个中,我希望该组吃掉!,留下@nimal 以匹配其余部分(它应该这样做)。相反,它似乎将!@ 组合在一起形成一个单词,通过以下匹配确认,

preg_match("/g\b!@\bn/i", "something!@nimal", $match);

知道为什么正则表达式会这样做吗?

我只是喜欢一个清楚地记录单词边界是如何确定的页面,我只是找不到一个适合我的生活。

【问题讨论】:

    标签: php regex


    【解决方案1】:

    单词边界\b 匹配从\w(单词字符)到\W 非单词字符的变化。如果在 @ 之前有一个 \b ,您想要匹配,这是一个 \W 字符。所以要匹配你的@之前需要一个单词字符

    something@nimal
            ^^
    

    ==> 由于g@ 之间的单词边界而匹配。

    something!@nimal
             ^^ 
    

    ==> 不匹配,因为!@ 之间没有单词边界,两个字符都是\W

    【讨论】:

    • 正如@hakre 在他的评论中所说,这就是 PCRE 处理单词边界的方式 (src)。谢谢你的澄清。
    • 是的,这是正确的答案。请注意,我冒昧地强调\b 不匹配字符,而是匹配位置。如果您不喜欢“编辑”,请随意回滚。
    • @Stephen Melrose,是的,hakre 发布了正确的链接,但是他/她的解释似乎有点不对劲(至少,我得到了印象)。当然,没有冒犯的意思。
    • 有趣的是,\b 很容易出错,并假设它在非单词字符之间匹配。即使你知道得更好,它也可能发生。我曾经在阅读有关该问题的整篇文章后一个小时犯过同样的错误。
    • 简单来说,\b 是一个位置,左边或右边都有一个单词。
    【解决方案2】:

    我在进行类似匹配时遇到的一个问题是像can'tit's 这样的单词,其中撇号被认为是单词/非单词边界(因为它与\W 匹配,而不是\w) .如果这对您来说可能是个问题,您应该排除撇号(以及有时出现的所有变体,例如 ' 和 '),例如通过创建一个类,例如[\b^'].

    您可能还会遇到真正属于单词一部分的 UTF8 字符(即我们人类对单词的含义)的问题,例如测试您的正则表达式与您对诸如 Svašek 之类的单词的编码方式。

    因此,在解析普通“语言”文本时,通常更容易查找“语言”边界,例如空格字符(不仅仅是字面意义上的空格,而是包括换行符和制表符在内的完整类)、逗号、冒号、句号、等(如果您正在解析 HTML,则使用尖括号)。 YMMV。

    【讨论】:

    • \b 放在像[\b^'] 这样的字符类中会匹配一个退格(ASCII 8)。相反,我们需要将\b 的快捷方式反编译为(?:(?<!\w)(?=\w)|(?<=\w)(?!\w)),然后在所有[\w^'] 中添加'。见stackoverflow.com/a/12712840/229088
    【解决方案3】:

    @ 不是单词字符的一部分(但是,在您的语言环境中,可能是默认 “单词”字符是任何字母或数字或下划线字符, Source - 所以@ 不是word 字符,因此不是\w 而是\W 并且链接任何\w\W\W\w 组合标记一个\b position),因此它始终是匹配的单词边界(在 OP 的正则表达式中)。

    以下内容与您的正则表达式类似,不同之处在于使用a 而不是a。并且行首也是一个单词边界,所以也不需要指定它:

    $r = preg_match("/\b(animal)/i", "somethinganimal", $match);
    var_dump($r, $match);
    
    $r = preg_match("/\b(animal)/i", "something!animal", $match);
    var_dump($r, $match);
    

    输出:

    int(0)
    array(0) {
    }
    int(1)
    array(2) {
      [0]=>
      string(6) "animal"
      [1]=>
      string(6) "animal"
    }
    

    【讨论】:

    • @Bart Kiers:PHP 正则表达式指的是 PCRE,\b 被描述为:“单词边界是主题字符串中当前字符和前一个字符不存在的位置都匹配 \w 或 \W(即一个匹配 \w,另一个匹配 \W),或者如果第一个或最后一个字符分别匹配 \w,则匹配字符串的开头或结尾。" src - \w 和 \W 也在其中进行了描述。当然@ 不能但可以是\w\W 的一部分。
    • @Hakre,我不确定你是不是这个意思,但你的回答表明\b 匹配@,这是错误的:\b 匹配一个位置,而不是一个字符.
    • 你是对的,@ 不是单词字符,不应该与\b 匹配,这就是我理解它应该工作的方式。但是,唉,在 PHP 中,他们决定让它以不同的方式工作:/
    • @Stephen,不,\b 从不匹配字符。它匹配两个字符之间的空字符串。请注意,PHP 对 \b 的解释与大多数其他流行的正则表达式实现 AFAIK 没有区别。 Perl、Java、Python等都是这样做的。
    • @ 匹配 \b,我的意思是检测到 @ 创建了一个边界,而不是匹配物理字符。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-06-07
    • 1970-01-01
    • 1970-01-01
    • 2020-02-07
    相关资源
    最近更新 更多