如何用PHP正则匹配目标词在指定距离内无特定词的句子?
用PHP正则实现「含目标词但排除特定邻近词(指定词距)」的句子匹配
这问题我之前做文本过滤时刚好碰到过,核心就是正向匹配目标词 + 负向环视排除特定邻近范围的词,用PHP的PCRE正则就能完美解决,我给你拆解思路和具体实现:
核心思路
我们需要两步逻辑:
- 确保句子里包含完整的目标词(避免部分匹配,比如
news里的new) - 用负向前瞻/后瞻,检查目标词前后N个词的范围内,是否存在需要排除的前置/后置词——如果存在,就跳过这个句子;如果不存在,就匹配成功
具体实现(可直接复用)
1. 配置参数
先把可变的条件抽成参数,方便后续调整:
<?php // 目标词:我们要找的词 $targetWord = 'new'; // 排除后置词:目标词后N词内不能出现的词 $excludeAfterWords = ['york', 'delhi']; // 排除前置词:目标词前N词内不能出现的词 $excludeBeforeWords = ['old', 'used']; // 最大词距:比如设置2,就是目标词前后最多2个词的范围(0=紧邻,1=隔1个词,以此类推) $maxWordDistance = 2;
2. 构建正则表达式
这里要注意转义正则特殊字符,避免词里有+*?这类符号导致正则出错:
// 转义排除词并拼接成正则分支(比如变成york|delhi) $excludeAfterPattern = implode('|', array_map('preg_quote', $excludeAfterWords)); $excludeBeforePattern = implode('|', array_map('preg_quote', $excludeBeforeWords)); // 拼接最终正则 $regex = sprintf( '~^.*\b(%s)\b(?!(?:\W+\w+){0,%d}\W+(?:%s))(?<!(?:\W+\w+){0,%d}\W+(?:%s)).*$~i', preg_quote($targetWord), $maxWordDistance, $excludeAfterPattern, $maxWordDistance, $excludeBeforePattern );
3. 测试匹配
拿一组句子测试看看效果:
$sentences = [ "I bought a new car yesterday.", // ✅ 匹配:new前后无排除词 "I'm visiting New York next week.", // ❌ 不匹配:new后1词是york "She found a new apartment near old town.",// ✅ 匹配:old在new后超过2词距 "Old new stock is not worth buying.", // ❌ 不匹配:old在new前紧邻 "Looking for new books about new delhi." // ✅ 匹配:第一个new符合条件(第二个new虽不符合,但句子存在有效匹配) ]; // 遍历匹配 foreach ($sentences as $sentence) { $matches = []; if (preg_match($regex, $sentence, $matches)) { echo "匹配成功:{$sentence}\n"; } else { echo "匹配失败:{$sentence}\n"; } }
关键细节解释
\b:词边界,确保我们匹配的是完整的new,而不是news、renew这类词里的片段(?:\W+\w+){0,$maxDistance}:匹配0到N个“分隔符+单词”的组合,也就是目标词前后最多N个词的范围。\W+负责匹配空格、逗号等分隔符,\w+匹配单词本身(?!...):负向前瞻,检查目标词后面N词内没有排除的后置词(?<!...):负向后瞻,检查目标词前面N词内没有排除的前置词preg_quote():转义目标词和排除词里的正则特殊字符(比如如果目标词是new?,会自动转义?避免正则语法错误)- 修饰符
i:忽略大小写,如果你需要区分大小写,可以去掉这个修饰符
进阶场景:要求所有目标词都不能有排除词
上面的代码是只要句子里有一个符合条件的目标词就匹配,如果你的需求是句子里所有目标词都不能在N词距内出现排除词,可以用下面的正则:
$regexStrict = sprintf( '~^(?!.*\b(%s)\b(?=(?:\W+\w+){0,%d}\W+(?:%s)))(?!.*(?<=(?:\W+\w+){0,%d}\W+(?:%s))\b(%s)\b).*\b(%s)\b.*$~i', preg_quote($targetWord), $maxWordDistance, $excludeAfterPattern, $maxWordDistance, $excludeBeforePattern, preg_quote($targetWord), preg_quote($targetWord) );
用这个正则的话,上面测试用的Looking for new books about new delhi.就会匹配失败,因为句子里有一个new后面跟着delhi。
内容的提问来源于stack exchange,提问作者Sathishkumar
相关产品推荐
相关产品推荐

