零宽断言正则表达式替换方案

2023年7月2日11:08:35

一、背景

safari浏览器不支持零宽断言正则表达式

二、解决方案

使用其他正则替换零宽断言正则(包含:(?<=)正向肯定预查、(?<!)正向否定预查、(?=)反向肯定预查、(?!)反向否定预查)

三、涉及场景

1、仅校验,不取值

如表单正则校验,如editor\src\editor\Editor\constant\todoPropsList.js:2093,此类场景可能需要重新编写(改动可能较大)正则, 无需经过zeroWidthRegPolyfill方法

 

2、校验,并取值

2.1、正则表达式不含g

如packages\editor\src\CompEvent\hooks\useFunctionValue.tsx:17,需注意改写表达式后,后续取值可能会跟随分组而变化,无需经过zeroWidthRegPolyfill方法

2.2、正则表达式含g

如editor\src\editor\Editor\components\pageDetail\CodeMirrorModal\CodeMirrorModal.jsx:87,将正则/(?<=actionMap(\['|\[|\.))[^.[\s"']+?(?==|\s|'|])/g,改为/actionMap(\['|\[|\.)([^.[\s"']+?)(=|\s|'|])+?/g ,使用zeroWidthRegPolyfill方法处理

零宽断言正则表达式替换方案

四、方法使用说明

涉及方法(主要处理正则含g的正则表达式):

packages\editor\src\utils\common.ts -> zeroWidthRegPolyfill(str, reg, n=1)

const zeroWidthRegPolyfill = (str, reg, n = 1) => {
  let result = null;
  const originRegStr = reg.toString();
  const regStr = originRegStr.replace(/^\/(.*)+?(\/|\/g)$/, '$1');
  const regWithoutG = new RegExp(regStr);
  if (originRegStr.endsWith('g')) {
    const arr = str.match(reg);
    if (!arr) {
      return result;
    }
    result = [];
    arr.forEach((it) => {
      result.push(it?.match(regWithoutG)?.[n]);
    });
  } else {
    result = str.match(regWithoutG);
  }
  return result;
};

1、reg含g,入参为校验字符串str、不含零宽的正则reg,以及需要真正匹配获取的字符所在的分组(必须对需要获取的字符分组)的序号n。str.match(reg)返回含目标字符的字符串数组,需要通过it?.match(regWithoutG)?.[n]二次处理,返回仅含目标字符的字符串数组

1.1、至于为啥要二次处理,这里需要知道match的使用及返回值,以下做简要对比:

 1.2、当然,使用matchAll能够一次性获取所有的匹配,并且返回带有分组信息的数组,但ie浏览器不支持

 

2、如传入不含g的表达式,则效果等同str.match(reg)。

3、使用该方法与零宽断言的区别

先看案例:

 

 可以发现,零宽实现匹配了3个结果,非零宽匹配了2个结果,这是因为“零宽正则”匹配完了之后,会从匹配到的字符开始继续匹配,预查不消耗字符

一般场景,其实是需要消耗字符的。但是,如果需要完全与零宽正则相匹配,则需要使用升级版方法。

const zeroWidthRegPolyfillPlus = (str, reg, n = 1) => {
  let result = null;
  const originRegStr = reg.toString();
  const regStr = originRegStr.replace(/^\/(.*)+?(\/|\/g)$/, '$1');
  const regWithoutG = new RegExp(regStr);
  let hasMatch = true;
  // 实现零宽预查补偿消耗字符,循环匹配
  // const loopReg = (_str, res) => {
  //   hasMatch = false;
  //   const nextStr = _str.replace(regWithoutG, function () {
  //     hasMatch = true;
  //     res.push(arguments[n]);
  //     return arguments[n + 1];
  //   });
  //   if (hasMatch) {
  //     loopReg(nextStr, res);
  //   }
  // };
  if (originRegStr.endsWith('g')) {
    result = [];
    // 实现补偿方法一:
    // loopReg(str, result);
    // 实现补偿方法二:
    let nextStr = str;
    while (hasMatch) {
      hasMatch = false;
      nextStr = nextStr.replace(regWithoutG, function () {
        hasMatch = true;
        result.push(arguments[n]);
        return arguments[n + 1];
      });
    }
  } else {
    result = str.match(regWithoutG);
  }
  return result;
};

// 目标:查找$$之间包裹的所有字符串
const str = '$abc$d$ef$';
// 1、零宽实现
const regZero = /(?<=\$).*?(?=\$)/g;
let result = str.match(regZero);
console.log('result1:', result);
// 返回 result = ['abc', 'd', 'ef']
// 此处需要将正向肯定预查进行分组,以便方法中能够将预查的字符消耗重新补上
const reg = /\$(.*?)(\$)/g;

result = zeroWidthRegPolyfillPlus(str, reg);
console.log('result2:', result);
// 返回 result = ['abc', 'd', 'ef']

  • 作者:xiange18
  • 原文链接:https://blog.csdn.net/weixin_43664308/article/details/128577277
    更新时间:2023年7月2日11:08:35 ,共 2540 字。