如何修改TypeScript正则表达式避免连续匹配丢失目标子串?
解决正则分割连续目标子串丢失匹配的问题
问题背景
我们需要实现字符串分割功能:将输入字符串按照指定集合wordsToBold中的子串分割为数组,要求匹配到的子串作为独立元素存在。例如:
- 当
wordsToBold = ["test"],输入字符串"hi this is a test string",分割结果应为["hi this is a ", "test", " string"]。
此前修改正则后已支持非空格分隔的子串匹配,但遇到子串连续出现的场景时会丢失匹配:
- 当
wordsToBold = ["foo", "bar"],输入字符串"Thisfoo is a foobar sentence bar",当前分割结果为["This", "foo", " is a ", "foo", " sentence ", "bar"],其中foobar中的bar未被匹配到。
问题原因
默认正则匹配是非重叠匹配:当匹配到foo后,正则指针会移动到foo的末尾位置,导致后续的bar被跳过,无法被检测到。
解决方案
通过零宽度断言或包含目标子串/普通文本的全局匹配实现,确保匹配不占用字符位置,或覆盖所有可能的匹配场景。
方案1:全局匹配法(推荐)
构建正则同时匹配"目标子串"或"非目标子串的连续字符",通过全局匹配直接得到分割后的数组:
// 转义正则特殊字符 escapeRegExp(text: string): string { return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } splitStringByTargetWords(input: string, wordsToBold: string[]): string[] { if (!wordsToBold.length) return [input]; // 转义所有目标子串,拼接为交替匹配模式 const escapedWords = wordsToBold.map(this.escapeRegExp); // 正则规则:匹配目标子串,或匹配任意字符直到下一个目标子串(非贪婪模式) const matchRegex = new RegExp(`(${escapedWords.join('|')})|(?:(?!${escapedWords.join('|')}).)+`, 'g'); // 执行匹配并过滤空元素 return input.match(matchRegex)?.filter(part => part.length > 0) || []; }
测试验证:
输入"Thisfoo is a foobar sentence bar",分割结果为["This", "foo", " is a ", "foo", "bar", " sentence ", "bar"],foobar被正确拆分为"foo"和"bar"两个元素。
方案2:零宽度分割法
利用正向/反向预查实现零宽度分割,分割时不消耗目标子串,从而支持连续匹配:
escapeRegExp(text: string): string { return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } splitStringByTargetWords(input: string, wordsToBold: string[]): string[] { if (!wordsToBold.length) return [input]; const escapedWords = wordsToBold.map(this.escapeRegExp); // 零宽度分割:在目标子串的前后位置分割 const splitRegex = new RegExp(`(?<=(${escapedWords.join('|')}))|(?=(${escapedWords.join('|')}))`, 'g'); // 分割后过滤空值和undefined return input.split(splitRegex).filter(part => part && part.length > 0); }
关键要点
- 正则转义:必须转义目标子串中的特殊字符(如
.、*),避免干扰正则语法解析。 - 零宽度断言:
(?=...)(正向预查)和(?<=...)(反向预查)不占用字符位置,确保连续子串都能被匹配。 - 全局匹配/分割:添加
g修饰符,确保遍历整个字符串处理所有匹配场景。
内容的提问来源于stack exchange,提问作者Jahson
相关产品推荐
相关产品推荐

