Lookbehind正则表达式在Safari中不兼容的替代实现方案咨询
适配Safari的Unicode运算符匹配方案
原实现问题
我原本使用以下代码检测运算符(and/or/not/exclude)与非英文字符的搭配使用:
/** * 生成支持Unicode的运算符正则模板 * * \b(单词边界)可展开为:(?:(?<=^)(?=\w)|(?<=\w)(?=$)|(?<=\W)(?=\w)|(?<=\w)(?=\W)) * \B(非单词边界)可展开为:(?:(?<=^)(?=\W)|(?<=\W)(?=$)|(?<=\W)(?=\W)|(?<=\w)(?=\w)) * 支持Unicode的\w匹配规则:[\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}] * */ const w = String.raw`[\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]`; const nw = String.raw`[^\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]`; const uwb = String.raw`(?:(?<=^)(?=${w})|(?<=${w})(?=$)|(?<=${nw})(?=${w})|(?<=${w})(?=${nw}))`; const unwb = String.raw`(?:(?<=^)(?=${nw})|(?<=${nw})(?=$)|(?<=${nw})(?=${nw})|(?<=${w})(?=${w}))`; const OPERATOR_REGEX = new RegExp( String.raw`(?!${unwb}"[^"“”]*)${uwb}(and|or|not|exclude)(?=.*\s)${uwb}(?![^"“”]*"${unwb})`, 'giu' ); const query1 = '(Java or "化粧" or 化粧品)'; const query2 = '(Java or 化粧 or 化粧品)'; console.log(query1.split(OPERATOR_REGEX)); console.log(query2.split(OPERATOR_REGEX));
但该实现依赖正则后向断言语法,而Safari浏览器不支持这类特性,导致代码无法正常运行。
替代解决方案
要兼容Safari,我们可以通过显式捕获前后上下文的方式替代后向断言,同时完整保留原有的Unicode边界判断和引号内内容不匹配的逻辑:
核心思路
- 将原本用后向断言匹配的边界规则,改为正向捕获分组的形式
- 自定义拆分函数处理正则匹配结果,过滤掉捕获的边界内容,只保留目标运算符和文本片段
- 保留原逻辑中「引号内的运算符不参与匹配」的规则,避免误拆分
兼容版代码实现
/** * 兼容Safari的Unicode运算符匹配方案 * 用正向捕获分组替代后向断言,实现相同的边界判断逻辑 */ const w = String.raw`[\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]`; const nw = String.raw`[^\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]`; // 定义前边界:用正向匹配替代后向断言,覆盖所有单词边界场景 const preBoundary = String.raw`(?:^(?=${w})|${nw}(?=${w})|${w}(?=${nw})|${w}$)?`; // 定义后边界:逻辑与前边界对称 const postBoundary = String.raw`(?:(?<=^)${w}|(?<=${w})${nw}|(?<=${nw})${w}|(?<=${w})$)?`; // 构建兼容版正则:将边界作为捕获分组,保留引号内不匹配规则 const OPERATOR_REGEX = new RegExp( String.raw`(?!${nw}?"[^"“”]*)(${preBoundary})(and|or|not|exclude)(${postBoundary})(?=\s)(?![^"“”]*"${nw}?)`, 'giu' ); function splitWithOperators(query) { const result = []; let lastIndex = 0; let match; while ((match = OPERATOR_REGEX.exec(query)) !== null) { // 加入运算符之前的文本片段 if (match.index > lastIndex) { result.push(query.slice(lastIndex, match.index)); } // 加入运算符本身(跳过捕获的边界分组) result.push(match[2]); lastIndex = OPERATOR_REGEX.lastIndex; } // 加入最后一段剩余文本 if (lastIndex < query.length) { result.push(query.slice(lastIndex)); } return result; } // 测试用例 const query1 = '(Java or "化粧" or 化粧品)'; const query2 = '(Java or 化粧 or 化粧品)'; console.log(splitWithOperators(query1)); console.log(splitWithOperators(query2));
方案说明
- 完全移除后向断言(
(?<=...)),改用正向前瞻和捕获分组实现边界判断 - 自定义拆分函数保证输出结果与原代码一致,过滤掉不需要的边界捕获内容
- 保留原逻辑中的引号内内容保护规则,避免误拆分引号里的文本
- 兼容所有不支持后向断言的浏览器,包括Safari
内容的提问来源于stack exchange,提问作者codingrohtak
相关产品推荐
相关产品推荐

