如何实现按搜索词输入顺序高亮,不标记无关位置的相同关键词
问题描述
我见过很多用简单的preg_replace甚至只用str_replace,给搜索词数组里的每个搜索词包裹<span class="highlight">标签实现搜索高亮的示例,但很多搜索场景需要匹配用户输入的关键词顺序,比如:
- 搜索词:
blue shoes - 对应的MySQL查询语句:
SELECT * FROM my_table WHERE title LIKE '%blue%shoes%' - 匹配规则:
- ✅ 匹配:"Don't step on my
bluesuedeshoes" - ❌ 不匹配:"... his fancy shoes and his baby blues..."(关键词顺序不符合要求)
- ✅ 匹配:"
blueshoesunder the blue sky"(第二个blue无需高亮) - ✅ 匹配:"... the
bluenotebook... hershoesand threw the blue, plastic horseshoe..."
- ✅ 匹配:"Don't step on my
搜索结果页(SERP)需要按照上述规则高亮匹配内容,请问是否可以通过正则表达式实现n个搜索词按顺序匹配高亮的需求?还是需要通过PHP循环来处理?
错误实现示例
foreach ( ['blue', 'shoes'] as $term ) { $result = str_replace($term, "<span style=\"background:yellow\">$term</span>", $title); } echo $result; // 该写法会高亮结果中任意位置出现的blue或shoes,不考虑顺序,不符合MySQL语句指定的顺序匹配查询要求
答案
完全可以通过单个正则表达式实现n个搜索词按顺序匹配高亮的需求,无需额外循环处理,性能和可维护性都比多次字符串替换更好。
实现思路
把用户输入的搜索关键词按空格拆分为非空数组,用.*?(惰性匹配任意字符)拼接成正则模式,每个关键词单独用捕获组包裹,替换时给每个捕获到的关键词套高亮标签即可,只会匹配首次按顺序出现的一组关键词,后续零散出现的关键词不会被高亮。
可直接使用的代码实现
/** * 按顺序匹配搜索词并高亮首个符合顺序的匹配组 * @param string $searchQuery 用户输入的原始搜索词 * @param string $content 待高亮的原文内容 * @param string $highlightTag 高亮标签模板,$1对应匹配到的关键词 * @return string 高亮处理后的内容 */ function highlightSequentialKeywords(string $searchQuery, string $content, string $highlightTag = '<span style="background: #ffff00">$1</span>'): string { // 拆分搜索词为非空数组,同时转义正则特殊字符避免正则注入 $keywords = array_filter( array_map('preg_quote', explode(' ', trim($searchQuery))), fn($keyword) => $keyword !== '' ); if (empty($keywords)) { return $content; } // 拼接正则模式:按顺序匹配关键词,中间任意字符用惰性匹配 $pattern = '/' . implode('(.*?)', array_map(fn($kw) => "($kw)", $keywords)) . '/iu'; // 构造替换规则:每个关键词套高亮标签,中间的原文内容保留 $replace = $highlightTag . implode('$2' . $highlightTag, array_fill(0, count($keywords) - 1, '')); return preg_replace($pattern, $replace, $content); }
测试效果
对应问题中的4个测试用例,调用方法如下:
$searchWord = 'blue shoes'; $testContents = [ "Don't step on my blue suede shoes", "... his fancy shoes and his baby blues...", "blue shoes under the blue sky", "... the blue notebook... her shoes and threw the blue, plastic horseshoe..." ]; foreach ($testContents as $content) { echo highlightSequentialKeywords($searchWord, $content) . "<br>"; }
运行后完全符合要求:
- 仅高亮首次按顺序出现的关键词,后续零散出现的同词不会被高亮
- 关键词顺序颠倒的内容不会触发任何高亮
- 支持任意数量的搜索关键词,自动适配规则
内容的提问来源于stack exchange,提问作者Phil Tune
相关产品推荐
相关产品推荐

