【问题标题】:How to match string, which does NOT contain a word?如何匹配不包含单词的字符串?
【发布时间】:2011-05-02 15:14:01
【问题描述】:

要匹配包含某个单词的字符串,我可以使用模式“/.*word.*/”。但是如何匹配一个不包含这个单词的字符串呢?

例子:

我需要在一个大文本中找到一个子字符串,它由两个标签和 , 包围,并且里面有一些像“Hello”这样的字符串。我想出的最好的:

"@<div>(.*?Hello.?*)</div>@i"

但它也会匹配序列:

<div>Bye.</div><div>Hello!</div>

而且我不想匹配第一对 div 标签 - 因此我想替换 ".*?"类似于“匹配任何字符串,除了不包含”。

测试用例

对于输入字符串:

<div>Bye.</div><div>Hello!</div>

我要赶上

<div>Hello!</div>

【问题讨论】:

  • @Dogbert - 这个问题很接近,但我怎样才能在子模式上使用那个东西?
  • 你想精确匹配什么?你能写一个测试用例吗?你说你不看匹配第一对 div 标签。但是第一对是不包含 Hello 的,这就是你的表达方式。那你怎么说你不想匹配不包含你想要排除的东西的东西呢?

标签: php regex regex-negation


【解决方案1】:

一个更好的问题标题可能是:“匹配包含特定子字符串的DIV 元素。” 首先必须说正则表达式不是这项工作的最佳工具。最好使用 HTML 解析器来解析标记,然后在每个 DIV 元素的内容中搜索所需的子字符串。也就是说,由于您不想了解更多关于如何使用正则表达式来匹配非其他内容的信息,因此以下描述了使用正则表达式执行此操作的有限方法。

正如 Dogbert 正确指出的那样,这个问题确实与 Regular expression to match string not containing a word? 重复。但是,我看到您已经查看了该问题,但需要知道如何将此技术应用于子模式。

要匹配不包含特定单词(或单词)的字符串(子模式)的一部分,您需要在每个字符之前应用否定的前瞻断言检查。以下是您将如何处理打开和关闭 DIV 标记之间的文本。请注意,当仅使用单个正则表达式时,因为 DIV 元素可能是嵌套的,所以只有在嵌套的 DIV 元素的“最里面”找到 "HELLO" 才合理。

伪代码:

  • 匹配开头的DIV标签。
  • 延迟匹配零个或多个字符,每个字符都不是&lt;div&lt;/div 的开头。
  • 找到所需的字符串:"HELLO" 后,继续匹配它。
  • 继续(贪婪地)匹配零个或多个字符,每个字符都不是&lt;div&lt;/div 的开头。
  • 匹配结束&lt;/div&gt;标签。

请注意,要仅匹配“最里面”的DIV 内容,必须在一次扫描元素内容时排除&lt;DIV&lt;/DIV 一个字符。以下是经过测试的 PHP 函数形式的相应正则表达式:

// Find an innermost DIV element containing the string "HELLO".
function p1($text) {
    $re = '% # Match innermost DIV element containing "HELLO"
        <div[^>]*>        # DIV element start tag.
        (?:               # Group to match contents up to "HELLO".
          (?!</?div\b)    # Assert this char is not start of DIV tag.
          .               # Safe to match this non-DIV-tag char.
        )*?               # Lazily match contents one chara at a time.
        \bhello\b         # Match target "HELLO" word inside DIV.
        (?:               # Group to match content following "HELLO".
          (?!</?div\b)    # Assert this char is not start of DIV tag.
          .               # Safe to match this non-DIV-tag char.
        )*                # Greedily match contents one chara at a time.
        </div>            # DIV element end tag.
        %six';
    if (preg_match($re, $text, $matches)) {
        // Match found.
        return $matches[0];
    } else {
        // No match found
        return 'no-match';
    }
}

此函数将正确匹配以下测试数据的所需 DIV 元素:

<div>Bye.</div><div>Hello!</div>

它还会在嵌套的 DIV 元素的最里面正确地找到“HELLO”:

<div>
    <div>
        Hello world!
    </div>
</div>

但是,如前所述,它不会找到位于非最内层嵌套 DIV 元素中的“HELLO”字符串,如下所示:

<div>
    Hello,
    <div>
        world!
    </div>
</div>

要做到这一点是一个复杂得多的解决方案。

在很多情况下,此解决方案可能会失败。再来一次。我建议使用 HTML 解析器。

【讨论】:

    【解决方案2】:
    '~<div>(?!.*?Bye\..*?</div>).+?</div>~'
    

    【讨论】:

    • @Rogach 这是 PHP 用户经常遇到的问题,我想知道为什么。我也想知道为什么答案并不广为人知。谢谢
    【解决方案3】:

    你不能只检查你是否没有匹配到吗?

    如果您要查找的不是“单词”一词:

    if(!preg_match("/word/i", $myString))
    

    这将在if 下运行代码,仅当找到“word”时。

    【讨论】:

    • 不,我不能。事实上,我需要从子模式中检查它。
    • 什么子模式...?你到底指的是什么?请更具描述性。
    • 我添加了一个例子来提问。
    • 这是正确答案,而不是您在“
      word
      ”中输入的“word”。如果您的意思是添加自定义包装器,请在进行预匹配之前计算您需要查找的内容
    • 第二。您不能指望仅使用 Regex 来完成所有事情,有时您需要在匹配之前/之后进行处理。
    猜你喜欢
    • 2022-11-19
    • 2012-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-27
    • 1970-01-01
    相关资源
    最近更新 更多