# pythonwise / num2eng.py

 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167``` ```#!/usr/bin/env python '''Convert number to English words \$./num2eng.py 1411893848129211 one quadrillion, four hundred and eleven trillion, eight hundred and ninety three billion, eight hundred and forty eight million, one hundred and twenty nine thousand, two hundred and eleven \$ Algorithm from http://mini.net/tcl/591 Modified by Chris Beaven to do add last "and" ''' __author__ = 'Miki Tebeka ' import math import re # Tokens from 1000 and up _PRONOUNCE = [ 'vigintillion', 'novemdecillion', 'octodecillion', 'septendecillion', 'sexdecillion', 'quindecillion', 'quattuordecillion', 'tredecillion', 'duodecillion', 'undecillion', 'decillion', 'nonillion', 'octillion', 'septillion', 'sextillion', 'quintillion', 'quadrillion', 'trillion', 'billion', 'million', 'thousand', '' ] # Tokens up to 90 _SMALL = { '0' : '', '1' : 'one', '2' : 'two', '3' : 'three', '4' : 'four', '5' : 'five', '6' : 'six', '7' : 'seven', '8' : 'eight', '9' : 'nine', '10' : 'ten', '11' : 'eleven', '12' : 'twelve', '13' : 'thirteen', '14' : 'fourteen', '15' : 'fifteen', '16' : 'sixteen', '17' : 'seventeen', '18' : 'eighteen', '19' : 'nineteen', '20' : 'twenty', '30' : 'thirty', '40' : 'forty', '50' : 'fifty', '60' : 'sixty', '70' : 'seventy', '80' : 'eighty', '90' : 'ninety' } def get_num(num): '''Get token <= 90, return '' if not matched''' return _SMALL.get(num, '') def triplets(l): '''Split list to triplets. Pad last one with '' if needed''' res = [] for i in range(int(math.ceil(len(l) / 3.0))): sect = l[i * 3 : (i + 1) * 3] if len(sect) < 3: # Pad last section sect += [''] * (3 - len(sect)) res.append(sect) return res def norm_num(num): '''Normelize number (remove 0's prefix). Return number and string''' n = int(num) return n, str(n) def small2eng(num): '''English representaion of a number <= 999''' n, num = norm_num(num) hundred = '' ten = '' if len(num) == 3: # Got hundreds hundred = get_num(num[0]) + ' hundred' num = num[1:] n, num = norm_num(num) if (n > 20) and (n != (n / 10 * 10)): # Got ones tens = get_num(num[0] + '0') ones = get_num(num[1]) ten = tens + ' ' + ones else: ten = get_num(num) if hundred and ten: return hundred + ' and ' + ten else: # One of the below is empty return hundred + ten def num2eng(num): '''English representation of a number''' small_first = None num = str(long(num)) # Convert to string, throw if bad number if (len(num) / 3 >= len(_PRONOUNCE)): # Sanity check raise ValueError('Number too big') if num == '0': # Zero is a special case return 'zero' # Create reversed list x = list(num) x.reverse() pron = [] # Result accumolator ct = len(_PRONOUNCE) - 1 # Current index for a, b, c in triplets(x): # Work on triplets p = small2eng(c + b + a) if small_first == None: small_first = a if p: pron.append(p + ' ' + _PRONOUNCE[ct]) ct -= 1 # Create result pron.reverse() # If there's no "hundred" in the last element, join the last 2 elements # with an "and" rather than a "," if len(pron) >= 2 and small_first and pron[-1].find('hundred') == -1: first = pron.pop() pron[-1] = '%s and %s' % (pron[-1], first) return ', '.join(pron) def main(argv=None): import sys from argparse import ArgumentParser argv = argv or sys.argv parser = ArgumentParser(description='') parser.add_argument('numbers', nargs='+') args = parser.parse_args(argv[1:]) for num in args.numbers: try: num = ''.join(re.findall('\d+', num)) print num2eng(num) except ValueError, e: print 'Error: %s' % e if __name__ == '__main__': main() ```