Roman Numerals Regex
Validates well-formed Roman numerals from 1 to 3999 written with standard subtractive notation (e.g. IV, IX, XL, XC, CD, CM).
Regex Pattern
^(?=[MDCLXVI])M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$Pattern Breakdown
Hover over a token to see what it does.
| Token | Meaning |
|---|---|
| ^ | Anchors the match to the start of the string |
| (?=[MDCLXVI]) | Lookahead requiring at least one valid Roman numeral character, rejecting an empty match |
| M{0,3} | Zero to three literal M characters, representing thousands (1000-3000) |
| (CM|CD|D?C{0,3}) | Hundreds place: CM (900), CD (400), or an optional D (500) followed by zero to three Cs (100 each) |
| (XC|XL|L?X{0,3}) | Tens place: XC (90), XL (40), or an optional L (50) followed by zero to three Xs (10 each) |
| (IX|IV|V?I{0,3}) | Ones place: IX (9), IV (4), or an optional V (5) followed by zero to three Is (1 each) |
| $ | Anchors the match to the end of the string |
Detailed Explanation
What it does
This pattern validates that a string is a syntactically correct Roman numeral in the range 1-3999, using standard additive and subtractive notation. It rejects malformed strings such as repeated subtractive pairs, too many repeated symbols, or numerals in the wrong order.
Why it works
Roman numerals are built from four place-value groups (thousands, hundreds, tens, ones), each of which has exactly one of a small set of valid spellings. The pattern models each group as its own alternation: a run of the base symbol repeated up to three times, an optional 'five' symbol prepended, or one of the two subtractive forms (four and nine). Concatenating the four groups in order and anchoring both ends means the whole string must be composed of exactly these valid group spellings with nothing left over. The leading lookahead prevents the pattern from accepting an empty string, since every group is independently optional.
Common use cases
- Validating Roman numeral input on outline/heading numbering tools
- Parsing chapter or clock-face numerals in document processing pipelines
- Form validation for historical or ceremonial date fields (e.g. movie copyright years, monarch ordinals)
- Sanitizing user input before converting Roman numerals to integers
Edge cases
- Numbers requiring four or more of the same symbol in a row, like 'IIII' for 4, are correctly rejected in favor of 'IV'
- Invalid subtractive combinations such as 'IL' (attempting 49) or 'VX' are rejected because only IV, IX, XL, XC, CD, and CM are recognized
- Lowercase input like 'xiv' will not match since the character classes are uppercase only; add the 'i' flag if lowercase should be accepted
- The pattern has no explicit upper bound check beyond structure, but structurally it cannot exceed 3999 (MMMCMXCIX) since M is capped at three repetitions
- An empty string is rejected by the leading lookahead rather than silently matching
Limitations
- Does not validate numerals above 3999, which historically use a vinculum (overline) notation not representable in plain ASCII
- Does not convert the numeral to its integer value; a separate parsing step is needed for that
- Case sensitivity means mixed-case input like 'Iv' will fail unless normalized or the 'i' flag is added
- Some historical or non-standard Roman numeral variants (e.g. 'IIIX' style seen on clocks) are intentionally rejected as non-canonical
Interactive Tester
Edit the pattern or text below — matching runs live in your browser.
Test Cases
Editable — add your own inputs to see if they pass.
| Input | Expected | Result | |
|---|---|---|---|
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass |
Language Variants
Production-ready examples in 12 languages.
const romanRegex = /^(?=[MDCLXVI])M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$/;
console.log(romanRegex.test('MCMXCIV')); // trueCommon Mistakes
Using a naive pattern like [IVXLCDM]+ that accepts any combination of letters, including invalid sequences like 'IIIIVV'
Fix: Model each place value (thousands, hundreds, tens, ones) as its own group with the correct additive/subtractive alternatives
Forgetting the leading lookahead and allowing the empty string to match since every group is independently optional
Fix: Add a lookahead like (?=[MDCLXVI]) to require at least one valid symbol before the optional groups
Assuming the regex also converts the numeral to an integer
Fix: Use the regex purely for validation and pair it with a separate lookup-based conversion function
Performance Notes
- Each place-value group is a bounded alternation with no nested quantifiers, so the pattern has linear-time matching with no catastrophic backtracking risk
- The {0,3} repetition counts are small and fixed, keeping the search space tiny even on pathological input
- Anchoring with ^ and $ plus the leading lookahead lets the engine reject non-Roman-numeral strings almost immediately
Browser Compatibility
| Engine | Supported | Notes |
|---|---|---|
| Chrome | Yes | — |
| Firefox | Yes | — |
| Safari | Yes | — |
| Edge | Yes | — |
| Node.js | Yes | — |