【JS】用正则匹配实现模糊搜索

本文介绍了如何利用正则表达式的正向预查功能实现前端的模糊搜索,包括支持不连续字符、乱序匹配和大小写模糊,通过哈希表统计字符数量,确保匹配的精确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、前话

二、介绍一种正则表达式:正向预查(positive lookahead)

三、实现模糊搜索

一、前话

在前端实现搜索功能的时候,大家经常用到的字符串匹配方法有如下几种:

  • String.indexOf(search_str)
  • String.includes(search_str)
  • 哈希表【利用哈希表记录搜索字符串和目标字符串中对应的单词,作匹配统计】

但是,以上这些方法,当遇到需要搜索字符串输入的字符可以  不一定要连续才能匹配、不一定要大小写匹配、字符之间可以乱序匹配  的模糊搜索时刻,那么用上述方法会增加复杂度

这里我们用强大的  正则表达式,来处理能符合上述要求的模糊搜索功能

二、介绍一种正则表达式:正向预查(positive lookahead)

(?=) 执行正则表达式中的正向预查。正向预查是一种零宽度断言,它不消耗自身字符去匹配,而是用于查看字符串中的 当前位置 之后是否能够匹配指定的模式

*注意,这里正向预查的括号,代表里面的预查都是独立的,是接着外部正则表达式指向的位置来查的,所以当多个预查连续排列是,预查开始的位置不是上一个预查结束的位置,而是括号外部指向的、此时匹配到的位置。利用这个性质,我们可以实现模糊搜索要求的乱序字符匹配

正则表达式中,除了正向预查外,还有其他预查方式,作出一个补充:

1、正向肯定预查 (?=pattern) 表示的是从其当前位置开始,预测后面的字符串必须匹配上pattern
2、正向否定预查 (?!pattern) 表示的是从其当前位置开始,预测后面的字符串必须匹配不上pattern
3、反向肯定预查 (?<=pattern) 表示的是从其当前位置开始,预测前面的字符串必须匹配上pattern
4、反向否定预查 (?<!pattern) 表示的是从其当前位置开始,预测前面的字符串必须匹配不上pattern

 

三、实现模糊搜索

其实最难的部分就是知道正向预查,并用其实现乱序匹配。另外的不一定连续匹配的要求,其实也包含在乱序匹配之中;大小写模糊,也直接可以通过regExp的匹配模式实现。

另外值得注意的是,匹配上了字符,还要控制字符数量一定是相同的。这里我们用了哈希表统计字符个数,将同一个重复的字符,我们合并在同一个正向预查中比如,我们aaa搜索字符串,如果用预查表达式(?=.*a)(?=.*a)(?=.*a),就会导致目标字符串"pang"也能匹配上,原因就是正向预查的独立性,从而对相同字符无法控制数量!!正确写法应该是(?=.*a.*a.*a)

综上,代码如下:

 //搜索用户
    const searchUser = function () {
        //支持模糊搜索
        let pattr = "^"
        let pre_look = "(?=.*"

        let word_map: any = {}
        //统计字符表,使得不仅要匹配上字符,字符数量是相同的
        nameVal.trim().split("").forEach((word) => {
            let lower = word.toLowerCase()
            if (word_map[lower]) {
                word_map[lower]++
            }
            else {
                word_map[lower] = 1
            }
        })

        //构造模式匹配字符串
        Object.keys(word_map).forEach((key) => {
            let num = word_map[key]
            pattr += pre_look
            for (let i = 0; i < num; i++) {
                if (i !== 0) pattr += ".*"
                pattr += key
            }
            pattr += ")"
        })
        pattr += ".*"

        let reg = new RegExp(pattr, 'i')
        //筛选出username字符串满足匹配格式的对象
        let filterData = tableData?.filter((item) => {
            return reg.test(item.username)
        }
        )
        console.log(filterData)
    }

<think>好的,我现在需要回答用户关于模糊匹配算法的问题。首先,用户提到了几个引用来源,我需要仔细看看这些引用内容,确保回答准确。引用[1]提到模糊匹配是其中的一种方法,而引用[2]和[3]分别提到了C++和Java的实现例子,以及时间复杂度的优化。用户想知道模糊匹配的概念和实现方式,所以需要从定义、常见算法、应用场景和优化方法这几个方面来组织回答。 首先,模糊匹配的定义。引用[1]里说模糊匹配允许一定程度的不完全匹配,比如拼写错误或格式差异。这一点需要明确,说明与精确匹配的区别。然后,常见的算法有哪些?引用[3]提到了最长公共子序列(LCS),这个算法可以用来比较两个字符串的相似度。另外,编辑距离(Levenshtein距离)也是常用的,但用户提供的引用中没有直接提到,不过根据我的知识,应该包括进去。还有N-gram相似度,比如引用[3]可能涉及到分词的匹配方式。此外,余弦相似度可能用于基于向量的匹配,比如文本处理中的TF-IDF方法。模糊字符串匹配库,比如引用[2]中的CMatch类,可能封装了这些算法,方便调用。 接下来是实现方式。需要分步骤说明,比如预处理、相似度计算和阈值设定。预处理可能包括转小写、去空格等标准化操作。相似度计算部分要举例子,比如编辑距离的计算方法,或者LCS的应用,引用[3]中的Java版优化时间复杂度为O(n*m)就是一个例子。阈值设定的话,比如编辑距离小于某个值就认为是匹配的。 应用场景方面,引用[1]提到了拼写纠错、数据清洗、搜索引擎和推荐系统。需要具体举例,比如用户输入错误时系统自动纠正,或者在数据清洗中合并重复记录。 优化方法方面,引用[3]提到将时间复杂度从指数级降到O(n*m),所以动态规划是一个优化手段。还可以提到预计算比如建立索引,或者分布式处理加速大规模数据匹配。 最后,根据用户提供的引用,引用[1]是总述,引用[2]和[3]是具体实现的例子,尤其是引用[3]详细说明了LCS的优化。这些都需要在回答中正确引用,确保每个引用标识符对应正确的内容,比如引用[3]在讲LCS优化时添加[^3]。 需要确保回答结构清晰,分点说明,并且自然融入引用标记。同时,用户要求使用中文,数学公式用$...$或$$...$$格式。比如在编辑距离部分,公式要用正确的LaTeX语法。例如,编辑距离的递推公式: $$d_{i,j} = \begin{cases} \max(i, j) & \text{if } \min(i, j) = 0 \\ \min \begin{cases} d_{i-1,j} + 1 \\ d_{i,j-1} + 1 \\ d_{i-1,j-1} + \text{cost} \end{cases} & \text{其他情况} \end{cases}$$ 需要检查LaTeX是否正确,并确保独立公式使用$$...$$。此外,用户要求生成相关问题,所以最后需要提出几个相关问题,比如优化方法、应用案例、不同算法比较等。 总结回答结构: 1. 模糊匹配的概念(与精确对比) 2. 常见算法(LCS、编辑距离、N-gram、余弦相似度、现有库) 3. 实现方式步骤(预处理、计算、阈值) 4. 应用场景 5. 优化方法(动态规划、预计算、分布式) 6. 引用正确标记 7. 相关问题 现在需要把这些内容用清晰的中文组织起来,确保每个部分自然衔接,引用标记在适当的位置。比如在提到LCS优化时,引用[3],在提到模糊匹配库时引用[2]等。同时避免使用Markdown格式,保持纯文本,但数学公式仍需要符合LaTeX要求。</think>### 模糊匹配的概念与实现方式 #### 1. 模糊匹配的定义 模糊匹配是一种允许**不完全一致但相似**的数据匹配技术,常用于处理拼写错误、格式差异、缩写等情况[^1]。例如,将“Hello World”与“hello_world”判定为匹配。 #### 2. 常见算法与实现 ##### (1) 最长公共子序列 (LCS) 通过计算两个字符串的最长公共子序列长度衡量相似性。 **实现优化**:动态规划将时间复杂度从$O(3^{\max(n,m)})$优化至$O(n \cdot m)$。 递推公式: $$dp[i][j] = \begin{cases} dp[i-1][j-1] + 1 & \text{if } s[i]=t[j] \\ \max(dp[i-1][j], dp[i][j-1]) & \text{其他} \end{cases}$$ ##### (2) 编辑距离 (Levenshtein距离) 计算将一个字符串转换为另一个字符串所需的最小操作次数(插入、删除、替换)。 公式: $$d_{i,j} = \begin{cases} \max(i, j) & \text{if } \min(i, j) = 0 \\ \min \begin{cases} d_{i-1,j} + 1 \\ d_{i,j-1} + 1 \\ d_{i-1,j-1} + \text{cost} \end{cases} & \text{其他情况} \end{cases}$$ ($\text{cost}=0$当字符相同,否则$\text{cost}=1$) ##### (3) N-gram相似度 将字符串分割为长度为$N$的片段,通过重叠片段的比例计算相似度。 例如,“apple”的2-gram为{`ap`, `pp`, `pl`, `le`}。 ##### (4) 模糊匹配库 如C++中的`CMatch`类可直接调用接口实现匹配[^2]: ```cpp CMatch matcher; bool result = matcher.Match(threshold, str1, str2); ``` #### 3. 实现步骤 1. **预处理**:标准化数据(转小写、去空格、统一编码)。 2. **相似度计算**:选择算法(如编辑距离)并设定阈值(如距离≤2则匹配)。 3. **结果过滤**:根据阈值返回匹配项。 #### 4. 应用场景 - **拼写纠错**:将“teh”纠正为“the”[^1]。 - **数据清洗**:合并“New York”和“NYC”等异构数据。 - **搜索引擎**:对模糊查询返回相关结果。 #### 5. 优化方法 - **动态规划**:降低时间复杂度(如LCS优化至$O(n \cdot m)$)。 - **预计算索引**:对常用字段建立哈希或倒排索引加速查询。 - **并行化**:分布式处理大规模数据匹配。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

音仔小瓜皮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值