Permalink
Cannot retrieve contributors at this time
81 lines (69 sloc)
2.63 KB
| """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 |