如何改进这个数字转文字脚本

3 投票
6 回答
561 浏览
提问于 2025-04-11 19:43
import sys

words = {
    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'
}

tens = [
    '',
    'twenty',
    'thirty',
    'forty',
    'fifty',
    'sixty',
    'seventy',
    'eighty',
    'ninety',
]

placeholders = [
    '',
    'thousand',
    'million',
    'billion',
    'trillion',
    'quadrillion'
]

# segMag = segment magnitude (starting at 1)
def convertTrio(number):
    return ' '.join([words[int(number[0])],  'hundred',  convertDuo(number[1:3])]) # convertDuo(number[1:3])


def convertDuo(number):
    #if teens or less
    if int(number[0]) == 1:
        return words[int(number)]
    #twenty-five
    else:
        return tens[int(number[0]) - 1] + '-' + words[int(number[1])]


if __name__ == "__main__":

    string = []
    numeralSegments = []
    numeral = sys.argv[1]

    if int(numeral) < 100:
        print convertDuo(numeral)
    else:

        # split number into lists, grouped in threes
        for i in range (0, len(numeral), 3):
            numeralSegments.append(numeral[i:i+3])

        numeralSegments.reverse()

        # for every segment, convert to trio word and append thousand, million, etc depending on magnitude
        for i in range (len(numeralSegments)):
            string.append(convertTrio(numeralSegments[i]) + ' ' + placeholders[i])

        # reverse the list of strings before concatenating to commas
        string.reverse()        
        print ', '.join(string)

警告: 我对Python完全是个新手。我知道可能有很多更高效的方法来完成这些事情。如果你能给我一些建议,我会非常感激。

编辑: 目前这段代码只适用于数字位数是三的倍数的情况。如果你能给我一个优雅的解决方案来修复这个问题,我也会很感激。谢谢。

6 个回答

2

与其去切分数字,不如用取模运算来分开个位数和十位数。这个函数可以把小于100的数字转换成你需要的格式。

def convert(n):
    q, r = divmod(n, 10)
    if q < 2:
        return words[n]
    result = tens[q-1] # offset because tens is missing first null value
    if r:
        result += '-' + words[r]
    return result

接着可以用递归的方法来处理更大的数字,比如可以先用 divmod(n, 100) 来开始,然后继续处理下去。

3

我想到两个改进的地方:

  • 40应该写成“forty”,而不是“fourty”
  • 你的程序需要单元测试

可以看看Python的doctestunittest模块。

2

你不能从左到右把数字分成“段”。使用 range(0,len(),3) 这样的写法是行不通的。你需要写一个算法来插入数字分隔符。这个过程是从右边开始,逐段取出数字。

剩下的部分(在左边,明白了吗?)会是1、2或3个数字。你有 convertTrioconvertDuo,分别处理3个和2个数字。里面还有一个处理1个数字的函数(我看不到)。

如果这不是作业的话,下面是一个合适的数字聚合算法。

def segment( n ):
   segList= []
   while len(n) > 3:
       segList.insert( 0, n[-3:] )
       n= n[:-3]
   segList.insert( 0, n )
   return segList

编辑

为了让代码更符合Python的风格,可以把它整理成一个整洁、可重用的模块。if __name__ == "__main__" 里面的内容做了两件事,这两件事应该分开。

你的命令行解析(任何与 sys.argv 相关的内容)是一回事。而实际的“转换数字”的功能则是另一回事。你希望看起来更像这样。

if __name__ == "__main__":
    import sys
    for number in sys.argv[1:]:
        print number2string( number )

这样,你的 number2string 函数就变成了这个模块中一个容易重用的部分。

撰写回答