# numberwords.py, V.2.0, Sep 17 2006 import re # for wordsToNumber def numberToWords(num, spacer=' '): "numberToWords(num, spacer=' '): convert a number to its written English name." # Just add names to grps list to extend naming capability. # Bengt Richter # http://mail.python.org/pipermail/python-list/2001-August/057835.html words = "fnord one two three four five six seven eight nine ten eleven twelve "\ "thirteen fourteen fifteen sixteen seventeen eighteen nineteen".split() # fnord is place holder words10 = "fnord fnord twenty thirty forty fifty sixty seventy eighty ninety".split() grps = ['', 'thousand', 'million', 'billion', 'trillion'] maxgrps = len(grps) if not num: return 'zero' if not isinstance(num, (int, long)): raise ArithmeticError,"Integer value required." if abs(num) >= 1000**maxgrps: raise ArithmeticError, "in numberToWords: num is too big." if num < 0: result = ['minus'] # or 'negative'? num = -num else: result = [] for grp in xrange(maxgrps): grpFactor, num = divmod( num, 1000**(maxgrps-1-grp) ) if grpFactor: hundreds, tens = divmod(grpFactor, 100) if hundreds: result.extend( [words[hundreds], 'hundred'] ) if 0 < tens < 20: result.append(words[tens]) elif tens > 0: tens, units = divmod(tens, 10) if tens and units: result.append( words10[tens] + spacer + words[units] ) elif tens: result.append( words10[tens] ) elif units: result.append( words[units] ) result.append( grps[maxgrps-1-grp] ) quot = num return spacer.join(filter(None, result)) _toNumber_dict = { 'and':0, 'zero':0, 'one':1, 'two':2, 'three':3, 'four':4, 'five':5, 'six':6, 'seven':7, 'eight':8, 'nine':9, 'ten':10, 'eleven':11, 'twelve':12, 'thirteen':13, 'fourteen':14, 'fifteen':15, 'sixteen':16, 'seventeen':17, 'eighteen':18, 'nineteen':19, 'twenty':20, 'thirty':30, 'forty':40, 'fifty':50, 'sixty':60, 'seventy':70, 'eighty':80, 'ninety':90, 'hundred':100, 'thousand':1000, } _spacer = re.compile( "|".join( sorted(_toNumber_dict, key=len, reverse=True) ) ) def wordsToNumber(text): """wordsToNumber(text): convert the written English name of a number to the number itself. >>> wordsToNumber("One thousand") 1000 >>> wordsToNumber("twenty-three") 23 >>> wordsToNumber("") 0 >>> wordsToNumber(" \\n") 0 >>> wordsToNumber("Three thousand, four hundredfiftyseven") 3457 >>> wordsToNumber("three thousand, four hundredfiftysette") Traceback (most recent call last): ... ValueError: Invalid element in input string three thousand four hundredfiftysette """ # Code by Stephen Thorne and Scott David Daniels, improved by leonardo maffi # http://groups.google.com/group/comp.lang.python/browse_thread/thread/118161ddcbc92a7c/ text2 = text.replace("-", " ").replace(",", " ").lower() stack = [0] def adder(mobj): num = _toNumber_dict[mobj.group()] if num >= 100: stack[-1] *= num if num >= 1000: stack.append(0) else: stack[-1] += num return "" if _spacer.sub(adder, text2).strip(): raise ValueError, "Invalid element in input string " + text2 return sum(stack) if __name__ == '__main__': # numberToWords tests n = 100 print "First %d numbers sorted alphabetically:" % n, numbers = sorted(xrange(n), key=lambda i: numberToWords(i, spacer="")) print " ".join(map(str, numbers)), "\n" # wordsToNumber tests for i in xrange(10000): assert wordsToNumber(numberToWords(i, "")) == i for i in xrange(50): print i, numberToWords(i, ""), wordsToNumber(numberToWords(i, "")) print # doctests too import doctest doctest.testmod() print "Tests finished."