You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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}&quot;[^&quot;“”]*)${uwb}(and|or|not|exclude)(?=.*\s)${uwb}(?![^&quot;“”]*&quot;${unwb})`,
  'giu'
);

const query1 = '(Java or &quot;化粧&quot; 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}?&quot;[^&quot;“”]*)(${preBoundary})(and|or|not|exclude)(${postBoundary})(?=\s)(?![^&quot;“”]*&quot;${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 &quot;化粧&quot; or 化粧品)';
const query2 = '(Java or 化粧 or 化粧品)';

console.log(splitWithOperators(query1));
console.log(splitWithOperators(query2));

方案说明

  • 完全移除后向断言((?<=...)),改用正向前瞻和捕获分组实现边界判断
  • 自定义拆分函数保证输出结果与原代码一致,过滤掉不需要的边界捕获内容
  • 保留原逻辑中的引号内内容保护规则,避免误拆分引号里的文本
  • 兼容所有不支持后向断言的浏览器,包括Safari

内容的提问来源于stack exchange,提问作者codingrohtak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 06:45:33