Regular expressions to match numbers (both regular and romans)

Viewed 48

I'm trying to write a regex to match both regular numbers (1, 2, 42...) and roman ones (X, VII...).

But the one I've currently wrote:

\b((?=[MDCLXVI])M{0,3}(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3}))\b|\b\d+\b

is matching more than expected. It has 9 matches, while I expect only 4:

  • XII
  • VII
  • 2
  • 12

How can I fix it?

2 Answers

You don't really need any lookahead in your regex.

Your regex can be simplified and refactored into this:

/
\b
   (?:
      [MDCLXVI]M{0,3}C[MD]
      |
      D?C{0,3}X[CL]
      |
      L?X{0,3}I[XV]
      |
      [XV]I{0,3}
      |
      I{1.3}
      |
      \d+
   )
\b
/gix

Updated RegEx Demo

Note that I have used x (extended mode) in regex so that regex will ignore all whitespaces which allows you to have proper indentation between multiple alternations to make your regex more readable. I don't know all permutations of roman number so I suggest you to please recheck each and every alternation.

The reason for that is the possibility of a zero-width match with just word boundary patterns (i.e.\b(?=[MDCLXVI])\b matches before any word starting with Roman number letter).

You need to precise the word boundaries, make the leading one match only before a word char, and the last one to match only after a word char:

(?<!\w)(?:(?=[MDCLXVI])M{0,3}(?:C[MD]|D?C{0,3})(?:X[CL]|L?X{0,3})(?:I[XV]|V?I{0,3})|\d+)(?!\w)

See the regex demo.

Here, (?<!\w) acts as a word boundary that fails the match if, immediately to the left of the current location, there is a word char, and (?!\w) acts a word boundary that fails the match if, immediately to the right of the current location, there is a word char.

Related