mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 11:23:31 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			80 lines
		
	
	
	
		
			2.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			80 lines
		
	
	
	
		
			2.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""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
 |