You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
81 lines
2.6 KiB
81 lines
2.6 KiB
"""Convert to and from Roman numerals""" |
|
|
|
__author__ = "Mark Pilgrim (f8dy@diveintopython.org)" |
|
__version__ = "1.4" |
|
__date__ = "8 August 2001" |
|
__copyright__ = """Copyright (c) 2001 Mark Pilgrim |
|
|
|
This program is part of "Dive Into Python", a free Python tutorial for |
|
experienced programmers. Visit http://diveintopython.org/ for the |
|
latest version. |
|
|
|
This program is free software; you can redistribute it and/or modify |
|
it under the terms of the Python 2.1.1 license, available at |
|
http://www.python.org/2.1.1/license.html |
|
""" |
|
|
|
import re |
|
|
|
#Define exceptions |
|
class RomanError(Exception): pass |
|
class OutOfRangeError(RomanError): pass |
|
class NotIntegerError(RomanError): pass |
|
class InvalidRomanNumeralError(RomanError): pass |
|
|
|
#Define digit mapping |
|
romanNumeralMap = (('M', 1000), |
|
('CM', 900), |
|
('D', 500), |
|
('CD', 400), |
|
('C', 100), |
|
('XC', 90), |
|
('L', 50), |
|
('XL', 40), |
|
('X', 10), |
|
('IX', 9), |
|
('V', 5), |
|
('IV', 4), |
|
('I', 1)) |
|
|
|
def toRoman(n): |
|
"""convert integer to Roman numeral""" |
|
if not (0 < n < 5000): |
|
raise OutOfRangeError, "number out of range (must be 1..4999)" |
|
if int(n) != n: |
|
raise NotIntegerError, "decimals can not be converted" |
|
|
|
result = "" |
|
for numeral, integer in romanNumeralMap: |
|
while n >= integer: |
|
result += numeral |
|
n -= integer |
|
return result |
|
|
|
#Define pattern to detect valid Roman numerals |
|
romanNumeralPattern = re.compile(""" |
|
^ # beginning of string |
|
M{0,4} # thousands - 0 to 4 M's |
|
(CM|CD|D?C{0,3}) # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's), |
|
# or 500-800 (D, followed by 0 to 3 C's) |
|
(XC|XL|L?X{0,3}) # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 X's), |
|
# or 50-80 (L, followed by 0 to 3 X's) |
|
(IX|IV|V?I{0,3}) # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's), |
|
# or 5-8 (V, followed by 0 to 3 I's) |
|
$ # end of string |
|
""" ,re.VERBOSE) |
|
|
|
def fromRoman(s): |
|
"""convert Roman numeral to integer""" |
|
if not s: |
|
raise InvalidRomanNumeralError, 'Input can not be blank' |
|
if not romanNumeralPattern.search(s): |
|
raise InvalidRomanNumeralError, 'Invalid Roman numeral: %s' % s |
|
|
|
result = 0 |
|
index = 0 |
|
for numeral, integer in romanNumeralMap: |
|
while s[index:index+len(numeral)] == numeral: |
|
result += integer |
|
index += len(numeral) |
|
return result |
|
|
|
|