如何匹配不包含某个单词的字符串?

4
匹配包含某个单词的字符串,可以使用模式“/.*word.*/”。但是如何匹配不包含此单词的字符串?
例如:
我需要在一个大文本中找到被两个标签和包围的子字符串,并且其中包含像“Hello”这样的字符串。我能想到的最好方法:
"@<div>(.*?Hello.?*)</div>@i"

但它也将匹配序列:
<div>Bye.</div><div>Hello!</div>

不想匹配第一对div标签,因此想用类似于“匹配任何字符串,但不包含”的东西来替换“.*?”。
测试用例:
对于输入字符串:
<div>Bye.</div><div>Hello!</div>

我需要抓住。
<div>Hello!</div>

1
可能是重复的问题:正则表达式匹配不包含某个单词的字符串? - Dogbert
@Dogbert - 那个问题很接近,但我如何在子模式上使用那个东西? - Rogach
你到底想要精确匹配什么?你能写一个测试用例吗?你说你不想匹配第一对 div 标签。但是第一对标签中没有包含 Hello,而这正是你在表达式中想要排除的内容。那么你怎么能说你不想匹配不包含你想要排除的内容的东西呢? - Janus Troelsen
3个回答

4
更好的问题标题可能是:"匹配包含特定子字符串的DIV元素。" 首先必须说明的是,正则表达式不是这项工作的最佳工具。最好使用HTML解析器解析标记,然后在每个DIV元素的内容中搜索所需的子字符串。尽管如此,既然您想了解如何使用正则表达式匹配不是其他东西的内容,下面将介绍一种有限的使用正则表达式的方法。
正如Dogbert所指出的那样,这个问题实际上是另一个问题的重复。但是,我看到您已经查看了那个问题,但需要知道如何将此技术应用于子模式。
要匹配不包含特定单词(或单词)的字符串部分(子模式),您需要在每个字符之前应用负向先行断言检查。以下是如何在打开和关闭DIV标记之间执行此操作的方法。请注意,当仅使用单个正则表达式时,由于DIV元素可以嵌套,因此只能在嵌套的DIV元素的“内部”找到"HELLO"
伪代码:
- 匹配打开的DIV标记。 - 懒惰地匹配零个或多个字符,每个字符都不是<div</div的开头。 - 一旦找到所需的字符串:"HELLO",请继续匹配它。 - 继续(贪婪地)匹配零个或多个字符,每个字符都不是<div</div的开头。 - 匹配关闭的</div>标记。
请注意,在逐个字符扫描元素内容时,为了仅匹配“最内层”的DIV内容,有必要同时排除<DIV</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解析器。


3
'~<div>(?!.*?Bye\..*?</div>).+?</div>~'

1
@Rogach 这是 PHP 用户经常问的问题,我想知道为什么。我也想知道为什么答案不被广泛知晓。谢谢。 - eyquem

0

你不能简单地检查一下,看看是否没有找到匹配吗?

如果你想要除了单词"word"以外的任何内容:

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

只有在未找到“word”时,此代码才会运行if下面的代码。


1
不,我不能。实际上,我需要从子模式中检查它。 - Rogach
什么子模式...?你具体指的是什么?请更加详细地描述。 - qJake
我在问题中添加了一个例子。 - Rogach
这是正确的答案,而不是“word”,你要放入“<div>word</div>”。如果你想添加自定义包装器,那么在执行preg匹配之前计算出你需要查找的内容。 - Abe Petrillo
同意。你不能指望仅凭正则表达式就能完成所有的事情,有时候你需要在匹配前/后进行处理。 - qJake

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接