You are here: Home > Dive Into Python > Regular Expressions > Case Study: Roman Numerals | << >> | ||||

## Dive Into PythonPython from novice to pro |

You've most likely seen Roman numerals, even if you didn't recognize them. You may have seen them in copyrights of old movies
and television shows (“Copyright `MCMXLVI`” instead of “Copyright `1946`”), or on the dedication walls of libraries or universities (“established `MDCCCLXXXVIII`” instead of “established `1888`”). You may also have seen them in outlines and bibliographical references. It's a system of representing numbers that really
does date back to the ancient Roman empire (hence the name).

In Roman numerals, there are seven characters that are repeated and combined in various ways to represent numbers.

`I`=`1``V`=`5``X`=`10``L`=`50``C`=`100``D`=`500``M`=`1000`

The following are some general rules for constructing Roman numerals:

- Characters are additive.
`I`is`1`,`II`is`2`, and`III`is`3`.`VI`is`6`(literally, “`5`and`1`”),`VII`is`7`, and`VIII`is`8`. - The tens characters (
`I`,`X`,`C`, and`M`) can be repeated up to three times. At`4`, you need to subtract from the next highest fives character. You can't represent`4`as`IIII`; instead, it is represented as`IV`(“`1`less than`5`”). The number`40`is written as`XL`(`10`less than`50`),`41`as`XLI`,`42`as`XLII`,`43`as`XLIII`, and then`44`as`XLIV`(`10`less than`50`, then`1`less than`5`). - Similarly, at
`9`, you need to subtract from the next highest tens character:`8`is`VIII`, but`9`is`IX`(`1`less than`10`), not`VIIII`(since the`I`character can not be repeated four times). The number`90`is`XC`,`900`is`CM`. - The fives characters can not be repeated. The number
`10`is always represented as`X`, never as`VV`. The number`100`is always`C`, never`LL`. - Roman numerals are always written highest to lowest, and read left to right, so the order the of characters matters very much.
`DC`is`600`;`CD`is a completely different number (`400`,`100`less than`500`).`CI`is`101`;`IC`is not even a valid Roman numeral (because you can't subtract`1`directly from`100`; you would need to write it as`XCIX`, for`10`less than`100`, then`1`less than`10`).

What would it take to validate that an arbitrary string is a valid Roman numeral? Let's take it one digit at a time. Since
Roman numerals are always written highest to lowest, let's start with the highest: the thousands place. For numbers 1000
and higher, the thousands are represented by a series of `M` characters.

### Example 7.3. Checking for Thousands

>>>import re>>>pattern = '^M?M?M?$'>>>re.search(pattern, 'M') <SRE_Match object at 0106FB58>>>>re.search(pattern, 'MM') <SRE_Match object at 0106C290>>>>re.search(pattern, 'MMM') <SRE_Match object at 0106AA38>>>>re.search(pattern, 'MMMM')>>>re.search(pattern, '') <SRE_Match object at 0106F4A8>

The hundreds place is more difficult than the thousands, because there are several mutually exclusive ways it could be expressed, depending on its value.

`100`=`C``200`=`CC``300`=`CCC``400`=`CD``500`=`D``600`=`DC``700`=`DCC``800`=`DCCC``900`=`CM`

So there are four possible patterns:

`CM``CD`- Zero to three
`C`characters (zero if the hundreds place is 0) `D`, followed by zero to three`C`characters

The last two patterns can be combined:

- an optional
`D`, followed by zero to three`C`characters

This example shows how to validate the hundreds place of a Roman numeral.

### Example 7.4. Checking for Hundreds

>>>import re>>>pattern = '^M?M?M?(CM|CD|D?C?C?C?)$'>>>re.search(pattern, 'MCM') <SRE_Match object at 01070390>>>>re.search(pattern, 'MD') <SRE_Match object at 01073A50>>>>re.search(pattern, 'MMMCCC') <SRE_Match object at 010748A8>>>>re.search(pattern, 'MCMC')>>>re.search(pattern, '') <SRE_Match object at 01071D98>

Whew! See how quickly regular expressions can get nasty? And you've only covered the thousands and hundreds places of Roman numerals. But if you followed all that, the tens and ones places are easy, because they're exactly the same pattern. But let's look at another way to express the pattern.

<< Case Study: Street Addresses |
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
Using the {n,m} Syntax >> |