以任何顺序匹配查询中的所有单词的正则表达式
Posted
技术标签:
【中文标题】以任何顺序匹配查询中的所有单词的正则表达式【英文标题】:Regular Expression to MATCH ALL words in a query, in any order 【发布时间】:2012-12-04 08:46:18 【问题描述】:我正在尝试为一个项目构建一个搜索功能,该功能根据用户搜索输入缩小项目范围,以及它是否与项目列出的关键字匹配。为此,我将项目关键字保存在 data
属性中,并使用 RegExp 模式将查询与这些关键字匹配。
我目前正在使用这个表达式,我知道它不正确,需要您的帮助:
new RegExp('\\b(' + query + ')', 'gi')))
其中查询是|
用户输入的查询的分隔值(例如\\b(meat|pasta|dinner)
)。即使只有 1 个匹配项,这也会返回一个匹配项,例如 - meat
只是为了抛出一些上下文,这里有一个小例子:
如果用户键入:meat pasta dinner
,它应该列出所有包含所有 3 个关键字的项目,即 meat
pasta
和 dinner
。它们与输入的顺序无关。
你能帮我用一个表达式来匹配查询中的所有单词,以任何顺序吗?
【问题讨论】:
嗯,不完全是 - 我需要将整个用户输入与项目的关键字进行匹配,并且只有在整个输入与这些项目上的关键字匹配时才返回 true。 基本上我需要想出一个 AND 表达式,它将与用户输入e.g. meat dinner pie
并检查关键字 e.g. Pasta meat continental kebab pie simple
。在这种情况下,表达式将失败,因为关键字包含 meat
和 pie
但不包含 dinner
。
为什么不使用简单的string
方法呢?我的意思是,如果没有重复的关键字,那么使用 Regex 可以轻松实现。但是对于重复的关键字,您最好在此处避免使用 Regex。只需将 indexOf
函数与 for 循环一起使用。或者,可能是一个更好的字符串函数,我可能不知道。
【参考方案1】:
你可以实现这将前瞻断言
^(?=.*\bmeat\b)(?=.*\bpasta\b)(?=.*\bdinner\b).+
看here on Regexr
(?=.*\bmeat\b)
是一个positive lookahead assertion,它确保\bmeat\b
在字符串中的某个位置。其他关键字也一样,.+
实际上匹配整个字符串,但前提是断言为真。
但它也会匹配“晚餐肉 Foobar 意大利面”
【讨论】:
echo "meat dinner pasta" | grep -P "^(?=.*\bmeat\b)(?=.*\bpasta\b)(?=.*\bdinner\b).+"
【参考方案2】:
你的正则表达式看起来不错:
\b(meat|pasta|dinner)\b
检查匹配的长度是否等于关键字的数量(在本例中为三个):
string.match(re).length === numberOfKeywords
其中re
是带有g
标志的正则表达式,string
是数据,numberOfKeywords
是关键字的数量
这假设没有重复的关键字。
【讨论】:
在这个模型中,重复项也需要删除 感谢您的帮助!但是,是的,关键字可能会重复,所以我将继续使用上述 stema 的答案。为逻辑+1。 此外,如果您需要至少“X”项的条件,其中“X”表示开始范围,那么您可以使用下面的正则表达式(针对至少一项修改)\b(meat|pasta|dinner)1 ,3\b【参考方案3】:根据接受的答案,我编写了一个简单的 Java 方法,该方法从一组关键字构建正则表达式
public static String regexIfAllKeywordsExists(String[] keywords)
StringBuilder sb = new StringBuilder("^");
for (String keyword : keywords)
sb.append("(?=.*\\b");
sb.append(keyword);
sb.append("\\b)");
sb.append(".+");
return sb.toString();
【讨论】:
感谢您的回答,但问题被标记为 javascript。【参考方案4】:stema 的回答在技术上是正确的,但它根本没有考虑性能。前瞻非常慢(在正则表达式的上下文中,快如闪电)。即使按照目前的逻辑,正则表达式也不是最优的。
因此,这里有一些测量值,是根据包含所有三个单词的较大字符串计算的,运行搜索 1000 次并使用四种不同的方法:
stema 的正则表达式
/^(?=.*\bmeat\b)(?=.*\bpasta\b)(?=.*\bdinner\b).+/
结果:605ms
优化的正则表达式
/^(?=.*?\bmeat\b)(?=.*?\bpasta\b)(?=.*?\bdinner\b)/
使用惰性匹配,不需要 end all 选择器
结果:291 毫秒
排列正则表达式
/(\bmeat\b.*?(\bpasta\b.*?\bdinner\b|\bdinner\b.*?\bpasta\b)|\bpasta\b.*?(\bmeat\b.*?\bdinner\b|\bdinner\b.*?\bmeat\b)|\bdinner\b.*?(\bpasta\b.*?\bmeat\b|\bmeat\b.*?\bpasta\b))/
结果:56ms
这很快,因为第一个模式正在匹配,如果最后一个模式匹配,它会比前瞻模式(300 ms)更慢
正则表达式数组
var regs=[/\bmeat\b/,/\bpasta\b/,/\bdinner\b/];
var result = regs.every(reg=>reg.test(text));
结果:26ms
注意,如果字符串被设计成不匹配,那么结果是:
521 毫秒 220ms 161ms - 慢得多,因为它必须经过所有分支 14 毫秒如您所见,在所有情况下,仅使用循环都会快一个数量级,更不用说更易于阅读了。
最初的问题是要求一个正则表达式,所以我的答案是排列正则表达式,但我不会使用它,因为它的大小会随着搜索词的数量呈指数增长。
此外,在大多数情况下,这个性能问题是学术性的,但有必要强调一下。
【讨论】:
以上是关于以任何顺序匹配查询中的所有单词的正则表达式的主要内容,如果未能解决你的问题,请参考以下文章