即使存在匹配项,while循环中的正则表达式exec也返回null

时间:2018-12-13 01:25:52

标签: javascript regex while-loop do-while

根据我的研究,正则表达式replace不是异步的。因此,对于为什么do-while循环中的匹配和替换有时无法匹配,我有些困惑。

我在下面创建了一个代码段,如果值中有任何匹配项,可以选择对其进行“双重检查”,但是我不知道为什么双重检查实际上阻止了它忽略匹配项。

您将在控制台中看到,injectableRegex.exec()do-while循环中运行两次后,它将正确替换所有匹配项。

更新:即使此方法也不一致,因为我发现有时doubleCheck部分正确匹配,然后随后的exec调用失败

const getInjectedPhrase = (phrase, injections, doubleCheck) => {
  let value = phrase;
  // only need to attempt to replace the injectables in the phrase is we've been passed injections
  if (injections && phrase.length > 1) {
    const injectableRegex = /{{\s?([\w-]+)\s?}}/g; // find any matching injectable, and extract its key
    let match;
    window.console.log('initial phrase:', phrase);
    // check if dictionary value contains injectable sections ie. sections surrounded by {{ }}
    do {
      // WHY IS THIS A THING!?
      if (doubleCheck) {
        injectableRegex.exec(value)
      }
      match = injectableRegex.exec(value);
      if (match) {
        /*
        match[0] -> {{ x }}
        match[1] -> x
        */
        const injectionValue = injections[match[1]];

        const injectionValueType = typeof injectionValue;
        if (
          injectionValueType === "string" ||
          injectionValueType === "number"
        ) {
          // globally replace the value with the injection's value
          value = value.replace(new RegExp(match[0], "g"), `${injectionValue}`);
          window.console.log('partial replace phrase:', value);
        }
      }
    } while (match !== null);
  }
  window.console.log('returned phrase:', value);
  return value;
};

window.console.log('WITHOUT DOUBLE CHECKING');
getInjectedPhrase(
  "foo {{partialCount}} of {{count}} bars", {
    partialCount: 3,
    count: 4
  },
  false
);
window.console.log('USING DOUBLE CHECKING');
getInjectedPhrase(
  "foo {{partialCount}} of {{count}} bars", {
    partialCount: 3,
    count: 4
  },
  true
);

1 个答案:

答案 0 :(得分:1)

问题在于正则表达式保留lastIndex属性,该属性跟踪最后一个匹配项的结束索引。在

foo {{partialCount}} of {{count}} bars

匹配

{{partialCount}}

随后将lastIndex属性设置为20-第二个}之后的位置。

然后,当您重新分配字符串以

foo 3 of {{count}} bars

使用相同的正则表达式尝试匹配将在该字符串的索引20 处开始,该字符串为{em> past {{count}}部分,因此匹配失败。

一种选择是每次将lastIndex手动重置为0:

const getInjectedPhrase = (phrase, injections, doubleCheck) => {
  let value = phrase;
  // only need to attempt to replace the injectables in the phrase is we've been passed injections
  if (injections && phrase.length > 1) {
    const injectableRegex = /{{\s?([\w-]+)\s?}}/g; // find any matching injectable, and extract its key
    let match;
    window.console.log('initial phrase:', phrase);
    // check if dictionary value contains injectable sections ie. sections surrounded by {{ }}
    do {
      injectableRegex.lastIndex = 0;
      match = injectableRegex.exec(value);
      if (match) {
        /*
        match[0] -> {{ x }}
        match[1] -> x
        */
        const injectionValue = injections[match[1]];

        const injectionValueType = typeof injectionValue;
        if (
          injectionValueType === "string" ||
          injectionValueType === "number"
        ) {
          // globally replace the value with the injection's value
          value = value.replace(new RegExp(match[0], "g"), `${injectionValue}`);
          window.console.log('partial replace phrase:', value);
        }
      }
    } while (match !== null);
  }
  window.console.log('returned phrase:', value);
  return value;
};

window.console.log('WITHOUT DOUBLE CHECKING');
getInjectedPhrase(
  "foo {{partialCount}} of {{count}} bars", {
    partialCount: 3,
    count: 4
  },
  false
);

一个更好的选择是使用回调函数一次replace ,而无需手动迭代,替换和重置正则表达式对象:

const getInjectedPhrase = (str, obj) => str.replace(
  /{{\s?([\w-]+)\s?}}/g,
  (_, key) => obj[key]
);

console.log(
  getInjectedPhrase(
    "foo {{partialCount}} of {{count}} bars", {
      partialCount: 3,
      count: 4
    },
  )
);