3

Trying to do what I thought was a fairly simple string replacement, but turns out to be more complicated than I thought.

If I have a string like

months + 3 + (startmonths * 3) + months - (months*7) + (monthsend*5)

For clarity, the "formula" I am parsing is user supplied, and can consist of any series of variables names and operators (+*/-) and parens that that a user can come up with. What I will need to do is first replace the variables with numbers and then evaluate the resulting expression.,

what I am looking for is how to replace all occurrences of the words months with, say "12" using string.replace function.

So hopefully the output of the function is

 12 + 3 + (startmonths * 3) + 12 - (12*3) + (monthsend*5)"

It seems like I need a regex to avoid replacing in strings like "startmonths", I am maybe under the impression its actually not possible to do in javascript regex because the "lookbehinds" are sparsely supported across modern browsers.

I tried using [^A-Za-z9-9_](months)(?:[^A-Za-z0-9_]) but that captures the character preceding and following 'months', so I can't use it as a parameter to string.replace.

Is there some workaround, or do I have to forget the replace function and do it "by hand" with find and splice etc?

Michał Turczyn
  • 32,028
  • 14
  • 47
  • 69
GGizmos
  • 3,443
  • 4
  • 27
  • 72

3 Answers3

3

This seems to work without needing look-behinds or look-aheads:

let regExMonth = /\bmonths\b/gm;
let str = "months + 3 + (startmonths * 3) + months - (months*7) + (monthsend*5)";

str = str.replace(regExMonth, "12");
console.log(str);

Screenshot from regexr.com: enter image description here

You're right that the look-behinds don't work everywhere yet. However, they do work in Chrome, and they'll be working in Firefox soon. Look-behinds were added in the 2018 specification, so it is shameful that they are not yet ubiquitous here in 2020.

Where look-behinds are supported, I'd use a both a "negative look-behind" and a "negative look-ahead" too like this:

(?<![A-Za-z0-9_])(months)(?![A-Za-z0-9_])

Shorthand of above would be:

(?<![\w])(months)(?![\w])

Screenshot from regexr.com: enter image description here

Lonnie Best
  • 9,936
  • 10
  • 57
  • 97
  • I will check out the link. I'm actually looking to use the regex to parse the meaning of user supplied "formulas" where there are "variables' like days months and hours, and the user supplies a vaslue like '(hours*6) + days', and also defines the values of hours and days elsewhere. Soirt of like a very simple spreadsheet formula parser without the functions. – GGizmos Jan 17 '20 at 05:48
0

you can use negative look-ahead and look-behind at the same time

const regex = /(?<![\w+])(months)(?![\w+])/gm;
const str = `months + 3 + (startmonths * 3) + months - (months*7) + (monthsend*5)`;
const subst = `12`;

const result = str.replace(regex, subst);
console.log('Substitution result: ', result);
pavithran G
  • 112
  • 2
  • 13
  • 1
    I like the `\w` shorthand for `[A-Za-z0-9_]`. However, the plus sign afterwards is probably unnecessary. For example, `(?<![\w])(months)(?![\w])` also accomplishes the task. – Lonnie Best Jan 17 '20 at 07:01
0

I can suggest (^|[^a-z])month

Explanation:

(^|[^a-z]) - match beginning of a string ^ or (due to alternation |) antyhing except letters due to negated character class [^a-z], finally, store it inside first capturing group.

month = match month literally

Demo

EDIT: small correction: (^|[^a-z])month($|[^a-z])

$- match end of string

Replace it with \112\2 - \1 - first cuptruing group, \2 - second capturing group

Michał Turczyn
  • 32,028
  • 14
  • 47
  • 69
  • In [my testing](https://i.stack.imgur.com/NnmtA.png), `(^|[^a-z])month` captures `(month` in `(monthsend*5)`, which is a false positive. – Lonnie Best Jan 17 '20 at 07:36
  • In [my tool](https://regexr.com/), adding `($|[^a-z])` to the end causes it not to match [anything](https://i.stack.imgur.com/Rr2kz.png). Interesting approach though. – Lonnie Best Jan 17 '20 at 08:32
  • @LonnieBest Bacuse it shouldn't match so it is correct. – Michał Turczyn Jan 17 '20 at 11:25