Solución:
Puede usar la siguiente expresión regular para esto:
^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$
Rompiéndolo M{0,4}
especifica la sección de miles y básicamente la restringe entre 0
y 4000
. Es relativamente simple:
0: <empty> matched by M{0}
1000: M matched by M{1}
2000: MM matched by M{2}
3000: MMM matched by M{3}
4000: MMMM matched by M{4}
Por supuesto, podrías usar algo como M*
permitir alguna número (incluido el cero) de miles, si desea permitir números más grandes.
El siguiente es (CM|CD|D?C{0,3})
, un poco más complejo, es para la sección de cientos y cubre todas las posibilidades:
0: <empty> matched by D?C{0} (with D not there)
100: C matched by D?C{1} (with D not there)
200: CC matched by D?C{2} (with D not there)
300: CCC matched by D?C{3} (with D not there)
400: CD matched by CD
500: D matched by D?C{0} (with D there)
600: DC matched by D?C{1} (with D there)
700: DCC matched by D?C{2} (with D there)
800: DCCC matched by D?C{3} (with D there)
900: CM matched by CM
En tercer lugar, (XC|XL|L?X{0,3})
sigue las mismas reglas que en la sección anterior pero para el lugar de las decenas:
0: <empty> matched by L?X{0} (with L not there)
10: X matched by L?X{1} (with L not there)
20: XX matched by L?X{2} (with L not there)
30: XXX matched by L?X{3} (with L not there)
40: XL matched by XL
50: L matched by L?X{0} (with L there)
60: LX matched by L?X{1} (with L there)
70: LXX matched by L?X{2} (with L there)
80: LXXX matched by L?X{3} (with L there)
90: XC matched by XC
Y finalmente, (IX|IV|V?I{0,3})
es la sección de unidades, manejo 0
mediante 9
y también similar a las dos secciones anteriores (los números romanos, a pesar de su aparente rareza, siguen algunas reglas lógicas una vez que averiguas cuáles son):
0: <empty> matched by V?I{0} (with V not there)
1: I matched by V?I{1} (with V not there)
2: II matched by V?I{2} (with V not there)
3: III matched by V?I{3} (with V not there)
4: IV matched by IV
5: V matched by V?I{0} (with V there)
6: VI matched by V?I{1} (with V there)
7: VII matched by V?I{2} (with V there)
8: VIII matched by V?I{3} (with V there)
9: IX matched by IX
Solo tenga en cuenta que esa expresión regular también coincidirá con una cadena vacía. Si no desea esto (y su motor de expresiones regulares es lo suficientemente moderno), puede usar la búsqueda hacia atrás y hacia adelante positivas:
(?<=^)M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})(?=$)
(la otra alternativa es simplemente verificar que la longitud no sea cero de antemano).
En realidad, tu premisa es defectuosa. 990 ES “XM”, así como “CMXC”.
Los romanos estaban mucho menos preocupados por las “reglas” que su maestro de tercer grado. Mientras sumase, estaba bien. Por lo tanto, “IIII” fue tan bueno como “IV” para 4. Y “IIM” fue completamente genial para 998.
(Si tiene problemas para lidiar con eso … Recuerde que la ortografía en inglés no se formalizó hasta el 1700. Hasta entonces, siempre que el lector pudiera entenderlo, era lo suficientemente bueno).
Solo para guardarlo aquí:
(^(?=[MDCLXVI])M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$)
Coincide con todos los números romanos. No le importan las cadenas vacías (requiere al menos una letra numérica romana). Debería funcionar en PCRE, Perl, Python y Ruby.
Demostración de Ruby en línea: http://rubular.com/r/KLPR1zq3Hj
Conversión en línea: http://www.onlineconversion.com/roman_numerals_advanced.htm