Regex exec в цикле while возвращает ноль, даже если есть совпадения - PullRequest
0 голосов
/ 13 декабря 2018

Из моего исследования регулярное выражение 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 голосов
/ 13 декабря 2018

Проблема в том, что регулярное выражение сохраняет свойство lastIndex, которое отслеживает конечный индекс последнего совпадения.В

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

сопоставление

{{partialCount}}

приводит к тому, что свойство lastIndex впоследствии устанавливается на 20 - местоположение после второго }.

Затем, когда вы переназначите строку на

foo 3 of {{count}} bars

, используя то же регулярное выражение, чтобы попытаться найти соответствие, начнется с индекса 20 этой строки, которая равна мимо часть {{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
    },
  )
);
...