Regular expression with looking back

regex

Solution

To arrive at the answer, I'll split it in simple steps.

1) Match a (math) b

For simplicity, we'll define a number as `\d+` which means match one digit or more. If you want a more comprehensive regex, you may take a look at this answer.

To match math operators, we might use a character class `[/*+-]`. If you put characters in a character class they lose their regex meaning, so `[.]` will only match a dot. We'll use different delimiters than `/`, that way we won't need to escape `/` in our expression. The hyphen `-` is often used to define a character range `a-z` but if you put it at the beginning or the end of the character class, you won't need to escape it.

Our regex will look like `\d+\s*[/*+-]\s*\d+`. `\s*` is there to match some whitespace(s) optionally.

Online demo

2) Match a (math) b (math) c (math) d

When using the above pattern, you'll realise that it matches only `a (math) b` and `c (math) d` whereas we want also to match `b (math) c`.

The problem

Let's take a simple example `1+2*3/4`, when the regex engine uses the following expression `\d+\s*[/*+-]\d+`:

1+2*3/4
^^^ match and advance

1+2*3/4
   ^ no match

1+2*3/4
    ^^^ match and advance

Nothing to do

So our problem is that the engine when it finishes one match, it will continue from the last character position + 1 while we want it to continue from the end of the first digit.

1+2*3/4
^^^ match and advance

1+2*3/4
 ^ continue from here ?

The solution

We'll need a zerowidth lookahead assertion `(?=)`. For example `a(?=b)` means if there is `b` after `a`, then match `a` so `a` gets matched in `ab` but not in `ac`. The advantage of this is that the regex engine will continue from position `b` instead of position `b` + 1.

ab
^ match and continue

ab
 ^ no match

We might exploit this and use a capturing group to "dump" the desired results in a group : `(?=(\d+\s*[/*+-]\d+))`.

1+2*3/4
^
^^^ match dump it in group 1 and continue

1+2*3/4
 ^ no match

1+2*3/4
  ^
  ^^^ match dump it in group 1 and continue

1+2*3/4
   ^ no match

1+2*3/4
    ^
    ^^^ match dump it in group 1 and continue

1+2*3/4
     ^ no match

1+2*3/4
      ^ no match

The end

Online demo

3) A wild problem appeared

So far so fine but when we tested some other digits we got some weird results. The input is `12+3` and it gave us two results in group 1 instead of one `12+3` and `2+3`. What's the reason ?

Well let's take a look step by step:

12+3
^
^^^^ match and dump it in group 1 and continue

12+3
 ^
 ^^^ match and dump it in group 1 and continue

12+3
  ^ no match

12+3
   ^ no match

Ah it seems like advancing with 1 step isn't good after all. So we need to match a number `(?=(\d+\s*[/*+-]\d+))\d+` !

12+3
^^
^^^^ match and dump it in group 1 and continue

12+3
  ^ no match

12+3
   ^ no match

A bit late for a TLDR, use `~(?=(\d+\s*[/*+-]\d+))\d+~` with the `g` modifier for some languages.

Depending on the language you might not be able to use custom delimiters which means you'll need to escape `/` in your expression.

Online demo

Problem

I need to find all operations in simple expression using regex. For ex: ``` a+b*c/d ``` Here we have 3 operations here. - a+b - b*c - c/d Regex like `\d.*[\+\-\*\/].*\d` returns only two matches. - a+b - c/d Is there any way to find all matches?

Original source